Modbus-RTU读取线圈

目录

一、基础信息

1.1、适用场景

1.2、数据类型

1.3、实现功能

1.4、调用非本文函数

二、主要程序内容

2.1、头文件声明内容

2.2、工程文件定义内容

2.3、子函数

三、使用示例

3.1、头文件声明内容

3.2、工程文件定义内容

3.3、使用示例语句


一、基础信息

1.1、适用场景

C程序Modbus-RTU模式,主机读取从机线圈状态时,从机生成响应数据的处理程序。

响应数据参照GB-T19582.1-2008《基于Modbus协议的工业自动化网络规范 第1部分:Modbus应用协议》。

1.2、变量类型

uint8_t,为8位无符号整形变量;

uint16_t,为16位无符号整形变量。

1.3、实现功能

使用十六进制(即0x0000~0xFFFF)表示线圈地址。地址高字节相等的线圈为同一扇区,如扇区1的线圈地址域为0x0000~0x00FF,扇区2的线圈地址域为0x0100~0x01FF。

本例程只列举扇区1和扇区2,其中扇区1共4个寄存器,扇区2共3个寄存器。

1.4、调用非本文函数

 CRC16_Modbus参照:Modbus查表法CRC校验程序_VIFIN的博客-CSDN博客C程序编写Modbus查表法的CRC校验。https://blog.csdn.net/VIFIN/article/details/125788125

 ModbusError参照:

Modbus错误码程序_VIFIN的博客-CSDN博客_modbus通讯协议错误代码C程序Modbus从机解码错误时,响应的错误数据。https://blog.csdn.net/VIFIN/article/details/126059724

二、主要程序内容

2.1、头文件声明内容

头文件主要声明线圈扇区,以及各扇区的寄存器功能。并定义一个用于存放所有线圈的二维数组Coils,二维数组的行数量为线圈扇区数量,二维数组的列数量是线圈数量最多的扇区的线圈数。

enum CoilsSection					//线圈扇区
{
	CoilsSection_01,				//线圈扇区1
	CoilsSection_02,				//线圈扇区2
									//根据需要扩展新的扇区
	CoilsSection_Number				//线圈扇区数量
};

enum DefineCoilsSection_01			//定义线圈扇区1
{
	Coil_01,						//实际使用填写线圈名称
	Coil_02,						//实际使用填写线圈名称
	Coil_03,						//实际使用填写线圈名称
	Coil_04,						//实际使用填写线圈名称
									//根据需要填写新的线圈名称
	CoilsSection_01_Number			//线圈扇区1数量
};
enum DefineCoilsSection_02			//定义线圈扇区1
{
	Coil_05,						//实际使用填写线圈名称
	Coil_06,						//实际使用填写线圈名称
	Coil_07,						//实际使用填写线圈名称
									//根据需要填写新的线圈名称
	CoilsSection_02_Number			//线圈扇区1数量
};

#define MaxCoilsSectionSize	CoilsSection_01_Number		//根据实际情况,定义线圈数量最多的扇区的线圈数量

extern uint16_t Coils[CoilsSection_Number][MaxCoilsSectionSize];

2.2、工程文件定义内容

工程文件只需要定义已经声明的二维数组Coils。

uint16_t Coils[CoilsSection_Number][MaxCoilsSectionSize];

2.3、子函数

子函数ReadCoilsCreateString,主要功能是生成读取线圈指令的响应数据。使用之前,需要排除所有错误情况。

/*
 * 功能:生成读取线圈成功的返回数据
 * 输入:	@p_read_data_string:读取数据的首指针地址
 * 		@p_retrun_data_string:返回数据的首指针地址
 * 		@p_return_data_size:返回字节长度的存放指针地址
 * 		@coils_section:线圈扇区
 * 		@coils_number:线圈数量
 * 输出:Modbus函数执行结果
 */
ModbusResult_TypeDef ReadCoilsCreateString(
		uint8_t* p_read_data_string,
		uint8_t* p_retrun_data_string,
		uint8_t* p_return_data_size,
		uint8_t  coils_section,
		uint8_t  coils_number
		)
{
	uint8_t i,h;
	uint8_t OutputByte = 0;
	uint16_t ReadCoilsNumber = 0;
	uint16_t crc_buffer = 0;

	ReadCoilsNumber = *(p_read_data_string + 4) * 256 + *(p_read_data_string + 5);		//计算:读取线圈长度
	OutputByte = (ReadCoilsNumber - 1) / 8 + 1;						//计算:返回数据字节数

	*(p_retrun_data_string + 0) = *(p_read_data_string + 0);		//返回缓存赋值:从机地址
	*(p_retrun_data_string + 1) = *(p_read_data_string + 1);		//返回缓存赋值:功能码
	*(p_retrun_data_string + 2) = OutputByte;						//返回缓存赋值:字节数

	ReadCoilsNumber = *(p_read_data_string + 3);					//赋值:读取线圈首地址

	for(h = 0;h < OutputByte;h ++)
	{
		for(i = 0;i < 8;i ++)
		{
			if(ReadCoilsNumber < coils_number)						//判定:读取线圈有效
			{
				if(Coils[coils_section][ReadCoilsNumber] == 0xFF00)	//判定:线圈为ON
				{
					*(p_retrun_data_string + 3 + h)  |= 0x80;
				}
			}

			*(p_retrun_data_string + 3 + h) >>= 1;					//左移一位
			ReadCoilsNumber ++;										//累加:读取线圈数量
		}
	}

	crc_buffer = CRC16_Modbus(3 + h, p_retrun_data_string);			//计算CRC

	*(p_retrun_data_string + 3 + h) = crc_buffer % 256;
	*(p_retrun_data_string + 4 + h) = crc_buffer / 256;

	*p_return_data_size = 5 + h;									//赋值:返回数据长度

	return Modbus_OK;												//返回:Modbus执行成功
}

p_read_data_string:读取数据的首指针地址,一般填写串口接收缓存数组的数组名;
p_retrun_data_string:返回数据的首指针地址,一般填写串口发送缓存数组的数组名;
p_return_data_size:返回字节长度的存放指针地址,一般填写发送长度缓存变量的指针地址;
coils_section:线圈扇区,直接填写所要读取的线圈扇区,填写内容为声明枚举“ CoilsSection”的声明内容;
coils_number:线圈数量,填写所要读取线圈扇区的线圈数量,填写内容为声明枚举“DefineCoilsSection_xx”的最后一个枚举元素名。

子函数ReadCoils,用于判断读取的线圈首地址和线圈数量是否符合要求。若不符合要求,则调用子函数“ModbusError”生成错误响应数据;若符合要求,则调用“ReadCoilsCreateString”生成正常响应数据。

/*
 * 功能:读取线圈的值
 * 输入:	@p_read_data_string:读取数据的首指针地址
 * 		@p_retrun_data_string:返回数据的首指针地址
 * 		@p_return_data_size:返回字节长度的存放指针地址
 * 输出:Modbus函数执行结果
 */
ModbusResult_TypeDef ReadCoils(
		uint8_t* p_read_data_string,
		uint8_t* p_retrun_data_string,
		uint8_t* p_return_data_size)
{
	uint16_t ReadCoilsNumber = 0;

	ReadCoilsNumber = *(p_read_data_string + 4) * 256 + *(p_read_data_string + 5);		//计算读取线圈长度

	if(ReadCoilsNumber == 0)															//判定:读取线圈长度为0
	{
		return ModbusError(
				RegisterNumberOrDataError,		//错误类型
				p_read_data_string,				//读取数据缓存
				p_retrun_data_string,			//返回数据缓存
				p_return_data_size);			//返回数据长度
	}
	else										//判定:线圈读取长度不为0
	{
		if(ReadCoilsNumber > 255)				//判定:线圈读取长度超过255
		{
			return ModbusError(
					RegisterNumberOrDataError,	//错误类型
					p_read_data_string,			//读取数据缓存
					p_retrun_data_string,		//返回数据缓存
					p_return_data_size);		//返回数据长度
		}
		else									//判定:线圈读取长度符合要求
		{
			switch(*(p_read_data_string + 2))
			{
				case CoilsSection_01:

					if(*(p_read_data_string + 5) > CoilsSection_01_Number)	//判定:读取寄存器数量过多
					{
						return ModbusError(
								RegisterAddressError,		//错误类型
								p_read_data_string,			//读取数据缓存
								p_retrun_data_string,		//返回数据缓存
								p_return_data_size);		//返回数据长度
					}
					else									//判定:读取寄存器数量符合要求
					{
						return ReadCoilsCreateString(
								p_read_data_string,			//读取数据缓存
								p_retrun_data_string,		//返回数据缓存
								p_return_data_size,			//返回数据长度
								CoilsSection_01,			//扇区
								CoilsSection_01_Number);	//扇区线圈数量
					}

				break;
				case CoilsSection_02:

					if(*(p_read_data_string + 5) > CoilsSection_02_Number)	//判定:读取寄存器数量过多
					{
						return ModbusError(
								RegisterAddressError,		//错误类型
								p_read_data_string,			//读取数据缓存
								p_retrun_data_string,		//返回数据缓存
								p_return_data_size);		//返回数据长度
					}
					else									//判定:读取寄存器数量符合要求
					{
						return ReadCoilsCreateString(
								p_read_data_string,			//读取数据缓存
								p_retrun_data_string,		//返回数据缓存
								p_return_data_size,			//返回数据长度
								CoilsSection_02,			//扇区
								CoilsSection_02_Number);	//扇区线圈数量
					}

				break;
				default:

					return ModbusError(
							RegisterAddressError,		//错误类型
							p_read_data_string,			//读取数据缓存
							p_retrun_data_string,		//返回数据缓存
							p_return_data_size);		//返回数据长度

			}
		}
	}
}

p_read_data_string:读取数据的首指针地址,一般填写串口接收缓存数组的数组名;
p_retrun_data_string:返回数据的首指针地址,一般填写串口发送缓存数组的数组名;
p_return_data_size:返回字节长度的存放指针地址,一般填写发送长度缓存变量的指针地址。

三、使用示例

3.1、头文件声明内容

串口部分声明内容:

#define ReceiveBufferSize	255			//接收缓存大小
extern uint8_t ReceiveBuffer[];

#define TransmitBufferSize	255			//发送缓存大小
extern uint8_t TransmitBuffer[];

extern struct UartVariable				//串口变量
{
	uint8_t receive_size;				//接收长度
	uint8_t transimt_size;				//发送长度
}
U1VE;

Modbus部分声明内容 :

enum ReadOrWriteRegisterErrorType	//读写寄存器错误类型
{
	FunctionCodeError = 1,			//错误功能码
	RegisterAddressError,			//寄存器地址错误
	RegisterNumberOrDataError,		//寄存器读取数量或写入值错误
	ReadOrWriteError,				//读写结果错误
};

typedef enum
{
	Modbus_OK,					//执行完成
	Modbus_ERROR,				//执行错误
	Modbus_NOP,					//空操作
}ModbusResult_TypeDef;			//Modbus程序执行结果

typedef enum
{
	CRC_Nop,							//未进行CRC校验
	CRC_Success,						//CRC校验成功
	CRC_Error,							//CRC校验错误
}CrcResult_TypeDef;						//CRC校验结果

3.2、工程文件定义内容

工程文件的串口定义内容:

uint8_t ReceiveBuffer[ReceiveBufferSize];           //定义ReceiveBuffer
uint8_t TransmitBuffer[TransmitBufferSize];			//定义TransmitBuffer

struct UartVariable	U1VE =		//串口1变量
{
	0,							//接收字节长度
	0,							//发送字节长度
};

3.3、使用示例语句

if(ModbusEnd() == CRC_Success)                                            //判断:CRC校验成功
{
    ReadCoils(ReceiveBuffer,TransmitBuffer,&U1VE.transimt_size);          //执行:生成响应数据

	HAL_UART_Transmit_IT(&huart1, TransmitBuffer, U1VE.transimt_size);    //执行:串口发送
	while(ModbusInitialize() == Modbus_ERROR);				              //初始化:串口1
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洪恒远

感君意气无所惜,一为歌行歌主客

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值