FreeMODBUS库 实现串口的选择切换

FreeMODBUS库 实现串口的选择切换

日期 作者 版本 说明
2020.12.16 Hxj V1.0 完成主体
2021.01.08 Hxj V1.0 精简后的版本


介绍

在FreeMODBUS 库中是不支持串口在运行的时候进行切换,同时也不支持多串口同时运行,必须得从新的配置才能进行串口的输出的选择。这样就使得程序有他的局限性。下文将会着重介绍如何进行选择性配置串口的输出。


一、FreeMODEBUS的串口配置

源码

头文件

/*
 * mb_user.h
 *
 *  Created on: May 24, 2020
 *      Author: 81001
 */

#ifndef FREEMODBUS_USER_MB_USER_H_
#define FREEMODBUS_USER_MB_USER_H_

#include "mb_user.h"
#include "mb.h"
#include "mbplatform.h"
#include "mb_coilsreg.h"
#include "mb_discretereg.h"
#include "mb_inputreg.h"
#include "mb_holdingreg.h"
#include "gpio_ext.h"

//是否启用FreeModbus模块
#define FREEMODBUS_ENABLE				1

//选择PORT类型是否为RS485模块
#define FREEMODBUS_PORT_INTERFACE_RS485
//#define FREEMODBUS_PORT_INTERFACE_RS232

//设置FreeModbus从机的地址
#define FREEMODBUS_DEV_ADDR	0x01
#define RS485_T_CTRL				GPIOE_OUT(15)

#define  FREEMODBUS_TIMER		2

extern uint8_t FreeModbus_PortNum;
extern USART_TypeDef *USART_Modbus;
extern uint32_t FreeModbus_PortBaudrate;
extern eMBParity Freemodbus_PortParity;
extern uint8_t DipValue;
extern uint8_t PORT_Switch;
void User_MB_InitPortParam(uint8_t portNum, uint32_t baudRate, eMBParity parity);
void User_MB_Config();
void User_MB_ConfigNVIC();
void User_MB_InitRegs();

void User_MB_RefreshInputRegister();
void User_MB_RefreshCoilsRegister();
void User_MB_RefreshDiscreteRegister();
void User_MB_RefreshHoldingRegister();

#endif /* FREEMODBUS_USER_MB_USER_H_ */

源文件

/*
 * mb_user.c
 *
 *  Created on: May 24, 2020
 *      Author: 81001
 */
#include "mb_user.h"
#include "sensor.h"
#include "user.h"
#include "tc.h"
#include "globalVariate.h"
#include "usart_ext.h"
#include "bldcmotor.h"
uint8_t FreeModbus_PortNum = 3;
USART_TypeDef *USART_Modbus = USART3;
uint32_t FreeModbus_PortBaudrate = 115200;
eMBParity Freemodbus_PortParity = MB_PAR_NONE;
uint8_t PORT_Switch = 0;

void User_MB_Config()
{
    
    
	switch (BitData_PortCtrl.ByteData)
	{
    
    
	//Bit0 = 0, configure DB-9 port
	case 0:
		//配置Modbus通讯串口的全局参数
		User_MB_InitPortParam(USART_DB9_NUM, 115200, MB_PAR_NONE);
		//配置GPIO
		USART_ConfigGPIO(USART_DB9_TXD_PORT, USART_DB9_TXD_PIN, USART_DB9_RXD_PORT, USART_DB9_RXD_PIN);
		//配置NVIC
		User_MB_ConfigNVIC();
		break;

		//Bit0 = 1, configure LCD port
	case 1:
		//配置Modbus通讯串口的全局参数
		User_MB_InitPortParam(USART_LCD_NUM, 115200, MB_PAR_NONE);
		//配置GPIO
		USART_ConfigGPIO(USART_LCD_TXD_PORT, USART_LCD_TXD_PIN, USART_LCD_RXD_PORT, USART_LCD_RXD_PIN);
		//配置NVIC
		User_MB_ConfigNVIC();
		break;
		//Bit0 = 0 & Bit1 = 1, configure RS485 port
	case 2:
		//Bit0 = 1 & Bit1 = 1, configure RS485 port
	case 3:
		//配置Modbus通讯串口的全局参数
		User_MB_InitPortParam(USART_485_NUM, 115200, MB_PAR_NONE);
		//配置GPIO
		USART_ConfigGPIO(USART_485_TXD_PORT, USART_485_TXD_PIN, USART_485_RXD_PORT, USART_485_RXD_PIN);
		//配置NVIC
		User_MB_ConfigNVIC();
		break;

	default:
		break;
	}
	//初始化Modbus四种寄存器
	User_MB_InitRegs();
	//初始化Modbus
	eMBInit(MB_RTU, FREEMODBUS_DEV_ADDR, FreeModbus_PortNum,
			FreeModbus_PortBaudrate, Freemodbus_PortParity);
	//使能Modbus功能
	eMBEnable();
}


void User_MB_RefreshCoilsRegister()
{
    
    
	//设置缓冲数组,提高轮询效率,只有缓冲数组中的数据发生变化的时候,才会实际寄存器的读写操作
	static uint8_t coilsReg_buffer[32] =
	{
    
     2 };

	(void) coilsReg_buffer;

	//开启串口切换
	if (coilsReg_buffer[24] != CoilsReg_GetBit(24))
	{
    
    
		coilsReg_buffer[24] = CoilsReg_GetBit(24);
		BitData_PortCtrl.BitData.bit1 = coilsReg_buffer[24];
	}
}

1.串口的选择配置函数 void User_MB_Config()解析
BitData_PortCtrl.ByteDataBitData_PortCtrl.BitData共用一块内存 所以可以直接用BitData_PortCtrl.ByteData进行判断。

/**
 * @brief 提供了一个快速配置并启动Modbus功能的模板(采用默认配置)
 * 		需要注意的是为了使此函数尽可能的简洁易用,并未设置函数参数。
 * 		用户需要根据自己项目的实际情况修改函数中的各个配置参数。
 */
void User_MB_Config()
{
    
    
	switch (BitData_PortCtrl.ByteData)
	{
    
    
	//Bit0 = 0, configure DB-9 port
	case 0:
		//配置Modbus通讯串口的全局参数
		User_MB_InitPortParam(USART_DB9_NUM, 115200, MB_PAR_NONE);
		//配置GPIO
		USART_ConfigGPIO(USART_DB9_TXD_PORT, USART_DB9_TXD_PIN, USART_DB9_RXD_PORT, USART_DB9_RXD_PIN);
		//配置NVIC
		User_MB_ConfigNVIC();
		break;

		//Bit0 = 1, configure LCD port
	case 1:
		//配置Modbus通讯串口的全局参数
		User_MB_InitPortParam(USART_LCD_NUM, 115200, MB_PAR_NONE);
		//配置GPIO
		USART_ConfigGPIO(USART_LCD_TXD_PORT, USART_LCD_TXD_PIN, USART_LCD_RXD_PORT, USART_LCD_RXD_PIN);
		//配置NVIC
		User_MB_ConfigNVIC();
		break;

		//Bit0 = 0 & Bit1 = 1, configure RS485 port
	case 2:
		//Bit0 = 1 & Bit1 = 1, configure RS485 port
	case 3:
		//配置Modbus通讯串口的全局参数
		User_MB_InitPortParam(USART_485_NUM, 115200, MB_PAR_NONE);
		//配置GPIO
		USART_ConfigGPIO(USART_485_TXD_PORT, USART_485_TXD_PIN, USART_485_RXD_PORT, USART_485_RXD_PIN);
		//配置NVIC
		User_MB_ConfigNVIC();
		break;

	default:
		break;
	}

	//初始化Modbus四种寄存器
	User_MB_InitRegs();
	//初始化Modbus
	eMBInit(MB_RTU, FREEMODBUS_DEV_ADDR, FreeModbus_PortNum,
			FreeModbus_PortBaudrate, Freemodbus_PortParity);
	//使能Modbus功能
	eMBEnable();
}

二.使用指南

思路说明:

本文实现的是通过使用硬件(拨码开关)来实现主串口的选择(DB9 或 LCD),主次串口的切换是通过一位线圈寄存器控制通过 写入0 1进行主次串口的切换。
串口的配置通过调用 void User_MB_Config()

使用步骤

1.在程序初始化阶段 通过一个位域操作 创建一个类似位操作的共用体,通过最小的空间来进行最多的操作。

typedef struct
{
    
    
	uint8_t bit0:1;
	uint8_t bit1:1;
	uint8_t bit2:1;
	uint8_t bit3:1;
	uint8_t bit4:1;
	uint8_t bit5:1;
	uint8_t bit6:1;
	uint8_t bit7:1;
} BitDataStruct;

typedef union
{
    
    
	BitDataStruct BitData;
	uint8_t ByteData;
} BitDataUnion;

2.在进入任务前 获取拨码开关的值将其保存在BitData_PortCtrl.BitData.bit0

	DipValue = User_GetDipSwitchValue();//读取拨码开关的值

	BitData_PortCtrl.BitData.bit0 = (DipValue & 0x01);//将其保存

//OFF==0   ON ==1
uint8_t User_GetDipSwitchValue()
{
    
    
		uint8_t value = 0x00;
		uint8_t dipSwitchBit1 = (~(*DIP_SWITCH_BIT_1)) & 0x01;
		uint8_t dipSwitchBit2 = (~(*DIP_SWITCH_BIT_2)) & 0x01;
		uint8_t dipSwitchBit3 = (~(*DIP_SWITCH_BIT_3)) & 0x01;
		uint8_t dipSwitchBit4 = (~(*DIP_SWITCH_BIT_4)) & 0x01;
		value = ((dipSwitchBit1 << 0) | (dipSwitchBit2 << 1) | (dipSwitchBit3 << 2) | (dipSwitchBit4 << 3)) & 0x0F;
		return value;
}

3.在FreeRTOS系统中创建一个任务进行串口的选择配置并进行轮询Modbus。

void ModbusPoll_task(void *pvParameters)
{
    
    
	User_MB_Config();//串口的初始化配置
	TickType_t ticks = xTaskGetTickCount();
	while (1)
	{
    
    
		eMBPoll();// modebus 的轮询
		//1ms 一个处理周期
		vTaskDelayUntil( &ticks, 1 );
	}
}

4.在刷新线圈寄存器中 轮询串口切换控制位是否发生改变。改变后将值赋值给BitData_PortCtrl.BitData.bit1

/**
 * @brief 刷新线圈寄存器,根据线圈寄存器的值执行对应动作
 * @param none
 * @retval none
 */
void User_MB_RefreshCoilsRegister()
{
    
    
	//设置缓冲数组,提高轮询效率,只有缓冲数组中的数据发生变化的时候,才会实际寄存器的读写操作
	static uint8_t coilsReg_buffer[32] =
	{
    
     2 };
	(void) coilsReg_buffer;
	//开启串口切换
	if (coilsReg_buffer[24] != CoilsReg_GetBit(24))
	{
    
    
		coilsReg_buffer[24] = CoilsReg_GetBit(24);
		BitData_PortCtrl.BitData.bit1 = coilsReg_buffer[24];
	}
}

5.在FreeRTOS系统中创建一个串口切换任务。轮询BitData_PortCtrl.BitData.bit1是否发生变化。改变后先将void ModbusPoll_task(void *pvParameters)任务删除 之后再进行创建新的void ModbusPoll_task(void *pvParameters)任务这样配置函数 void User_MB_Config()将进行重新的选择。

void  PortSwitch_task(void *pvParameters)
{
    
    
	static uint8_t Port=0;
	TickType_t ticks = xTaskGetTickCount();
	while(1)
	{
    
    
		if(Port != BitData_PortCtrl.BitData.bit1)
		{
    
    
			Port = BitData_PortCtrl.BitData.bit1;

			taskENTER_CRITICAL();			//进入临界区

			eMBDisable();
			vTaskDelete(ModbusPollTask_Handler); //删除ModbusPollTask_Handler任务

			xTaskCreate((TaskFunction_t )ModbusPoll_task,
					(const char*    )"ModbusPoll_task",
					(uint16_t       )MODBUS_POLL_STK_SIZE,
					(void*          )NULL,
					(UBaseType_t    )MODBUS_POLL_TASK_PRIO,
					(TaskHandle_t*  )&ModbusPollTask_Handler);

			taskEXIT_CRITICAL();				//退出临界区
		}
		vTaskDelayUntil( &ticks, 500);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_45531642/article/details/111249081