Modbus 协议传输数据,简易算法可同时实现发送一个或连续发送多个数据

1、功能码

        简单说几个常用的功01H 03H 06H

1.1、01H

01功能码(读状态)
地址 功能码 起始地址高位 起始地址低位 寄存器数量高位 寄存器数量低位 CRCH CRCL
0x01 0x01 0x00 0x00 0x00 0x05 FC 09
回应01功能码
地址 功能码 字节数 数据 CRCH CRCL
0x01 0x01 0x01 0x15  (0001 0101) 90 47

Modbus 协议传输数据,简易算法可同时实现发送一个或连续发送多个数据_第1张图片Modbus 协议传输数据,简易算法可同时实现发送一个或连续发送多个数据_第2张图片

数据计算说明
寄存器地址 数据
0x00 1
0x01 0
0x02 1
0x03 0
0x04 1
0x05 补0
0x06 补0
0x07 补0

 发送的数据是一个字节,需要0x00-0x04的数据,不足的补0,则0001 0101   (0x15)

1.2、03H

03功能码(读数据)
地址 功能码 起始地址高位 起始地址低位 寄存器数量高位 寄存器数量低位 CRCH CRCL
0x01 0x03 0x00 0x00 0x00 0x02 0B C4
回应03功能码
地址 功能码 字节数 数据1高位 数据1低位 数据2高位 数据2低位 CRCH CRCL
0x01 0x03 0x04       00         12        00         34 5B E1

每个数据两个字节,所以字节数为0x04

1.3、06H

06功能码(写数据)
地址 功能码 寄存器地址高位 寄存器地址低位 写入数据高位 写入数据低位 CRCH CRCL
0x01 0x06 0x00 0x00 0x00 0x02 08 0B
回应06功能码
地址 功能码 寄存器地址高位 寄存器地址低位 写入数据高位 写入数据低位 CRCH CRCL
0x01 0x06 0x00 0x00 0x00 0x02 08 0B

2、代码

                                                               modbus协议寄存器地址与状态

地址

参数定义

数据类型

数据字节

功能码

0x00 状态1

只读

1

01H

0x01 状态2

只读

1

01H

0x02 状态3

读/写

1

01H、06H

0x03 状态4

读/写

1

01H、06H

0x04 状态5

读/写

1

01H、06H

0x10 数据1

只读

2

03H

0x11 数据2

只读

2

03H

0x12 数据3

只读

2

03H

0x13 数据4

只读

2

03H

0x14 数据5

只读

2

03H

0x15 数据6

只读

2

03H

0x16 数据7

只读

2

03H

0x20

从机地址

只写

1

06H

#define data_value_Num 7  // 7个数据 每个数据两个字节
#define data_Status_Num 5  // 5个状态 每个状态一个字节
uint8_t addr_slave=0x01;//主机地址
uint8_t DATA_Status_code[data_Status_Num] = {1,1,1,0,0};// 状态码缓存区 用于01功能码读取再发送
// 地址 0x00-0x04
uint8_t DATA_value[data_value_Num*2] = {0x00,0x02,  0x00,0x04,  0x00,0x06,  0x00,0x08,  0x30,0x39,  0x30,0x39,  0x01,0x15,};// 数据缓存区,用于03功能码读取再发送
// 地址 0x10-0x16
uint8_t DATA01_TX[6] = {0X01,0X01,0x01};            // 用于01功能码发送
uint8_t DATA03_TX[data_value_Num*2+5]= {0x01,0x03};  // 用于03功能码发送  14字节数据+5个固定数据(地址、功能码、字节数、CRC)
uint8_t DATA06_TX[8] = {0X01,0X06};            // 用于功能码0x06发送

 定义几个数组用于存放数据与发送的数据

#ifndef F_CPU
#define  F_CPU 11059200UL
#endif

#include         // 端口
#include        // 看门狗
#include  // 中断
#include      // 延时
#include   
#include           // 配置好的GPIO库调用方便
#include         // 端口
#include 
#include 
#define DE_OUT       DDRA|=(1<<(7))    // 输出
#define DE_LOW       GPIO_LOW('A',7)   // 片选DE=0;
#define DE_HIGH      GPIO_HIGH('A',7)  // 片选DE=1;

#define data_value_Num 7  // 7个数据 每个数据两个字节
#define data_Status_Num 5  // 5个状态 每个状态一个字节
uint8_t addr_slave=0x01;//主机地址
uint8_t DATA_Status_code[data_Status_Num] = {1,1,1,0,0};// 状态码缓存区 用于01功能码读取再发送
// 地址 0x00-0x04
uint8_t DATA_value[data_value_Num*2] = {0x00,0x02,  0x00,0x04,  0x00,0x06,  0x00,0x08,  0x30,0x39,  0x30,0x39,  0x01,0x15,};// 数据缓存区,用于03功能码读取再发送
// 地址 0x10-0x16
uint8_t DATA01_TX[6] = {0X01,0X01,0x01};            // 用于01功能码发送
uint8_t DATA03_TX[data_value_Num*2+5]= {0x01,0x03};  // 用于03功能码发送  14字节数据+5个固定数据(地址、功能码、字节数、CRC)
uint8_t DATA06_TX[8] = {0X01,0X06};            // 用于功能码0x06发送
void clc_Uart_Buffer()  // 清串口缓存
{
	uint8_t x;
	for (x=0;x<8;x++)
		Uart_Buffer[x]=0;
}
void modbus_parse()  // modbus 数据解析
{
	uint8_t x;
	if (Uart_Buffer[0]==addr_slave)//地址判断
	{
		// 对接收到的串口数据做CRC校验
		crc_high8=crc16(Uart_Buffer,6)>>8;//crc高8位
		crc_low8=crc16(Uart_Buffer,6) & 0xFF;//crc低8位
		
		if ( crc_high8==Uart_Buffer[6] && crc_low8==Uart_Buffer[7] )  // 校验
		{
			if (Uart_Buffer[1]==1)// 功能码01  读状态
			{
				if (Uart_Buffer[5]-1<=0x04-Uart_Buffer[3])  // 连读超过最大地址则退出 列0x01,0x01,0x00,0x00,0x00,0x06  从地址0连读6个超过了5个数据则退出 
				{
					switch(Uart_Buffer[5])
					{  // 起始地址Uart_Buffer[3]
						case 1: DATA01_TX[3]=DATA_Status_code[Uart_Buffer[3]];break;//只读一个状态
						case 2: DATA01_TX[3]=DATA_Status_code[Uart_Buffer[3]+1]*2+DATA_Status_code[Uart_Buffer[3]];break;//只读两个个状态
						case 3: DATA01_TX[3]=DATA_Status_code[Uart_Buffer[3]+2]*4+DATA_Status_code[Uart_Buffer[3]+1]*2+DATA_Status_code[Uart_Buffer[3]];break;//只读三个状态
						case 4: DATA01_TX[3]=DATA_Status_code[Uart_Buffer[3]+3]*8+DATA_Status_code[Uart_Buffer[3]+2]*4+DATA_Status_code[Uart_Buffer[3]+1]*2+DATA_Status_code[Uart_Buffer[3]];break;//只读四个状态
						case 5: DATA01_TX[3]=DATA_Status_code[Uart_Buffer[3]+4]*16+DATA_Status_code[Uart_Buffer[3]+3]*8+DATA_Status_code[Uart_Buffer[3]+2]*4+DATA_Status_code[Uart_Buffer[3]+1]*2+DATA_Status_code[Uart_Buffer[3]];break;//只读五个状态
						default:break;
					}
					DATA01_TX[4]=crc16(DATA01_TX,4)>>8;
					DATA01_TX[5]=crc16(DATA01_TX,4) & 0xFF;
					DE_HIGH;
					usart_send_array(DATA01_TX,6);
					DE_LOW;
				}
			}
			else if (Uart_Buffer[1]==6)// 功能码06  写状态 与 该从机地址
			{
				if (Uart_Buffer[3]<5 && (Uart_Buffer[5]==0||Uart_Buffer[5]==1)) // 地址小于5 && 改的数据只能为0和1
				{
					DATA_Status_code[Uart_Buffer[3]]=Uart_Buffer[5];//把值赋值给需要修改的地址
					DATA06_TX[3]=Uart_Buffer[3];//赋值发送地址
					DATA06_TX[5]=DATA_Status_code[Uart_Buffer[3]];//赋值改的数据
				}
				else if (Uart_Buffer[3]==0x20 && Uart_Buffer[5]<128)// 修改地址 && 小于128
				{
					addr_slave=Uart_Buffer[5];        // 修改地址
					DATA06_TX[3]=Uart_Buffer[3];// 赋值发送地址
					DATA06_TX[0]=addr_slave;
				}
				DATA06_TX[6]=crc16(DATA06_TX,6)>>8;
				DATA06_TX[7]=crc16(DATA06_TX,6) & 0xFF;
				DE_HIGH;
				usart_send_array(DATA06_TX,8);
				DE_LOW;
			}
			else if (Uart_Buffer[1]==3)// 功能码03 读数据
			{
				if (Uart_Buffer[3]>=0x10&&Uart_Buffer[3]<=0x16&&Uart_Buffer[5]-1<=0x16-Uart_Buffer[3])//不超出地址&&不超过最大个数
				{
					DATA03_TX[2]=Uart_Buffer[5]*2;     // 发送字节数  每个数据两个字节
					for (x=0;x>8;     // 校验高位
					DATA03_TX[Uart_Buffer[5]*2+4]=crc16(DATA03_TX,Uart_Buffer[5]*2+3) & 0xFF; // 校验低位
					DE_HIGH;
					usart_send_array(DATA03_TX,Uart_Buffer[5]*2+5);  // 发送
					DE_LOW;
				}
			}
		}
	}
	clc_Uart_Buffer();  // 清串口缓存
}
int main()
{
	init_USART();   // USART 初始化
	Time0_init();               // 定时器0初始化
	DE_OUT;
	DE_LOW ;
	while (1)
	{
		DATA01_TX[0]=addr_slave;
		DATA03_TX[0]=addr_slave;
		DATA06_TX[0]=addr_slave;
		
		if (star_parse_flag==1)
		{
			star_parse_flag=0;
			modbus_parse();  // modbus 数据解析
		}
	}
}

你可能感兴趣的:(AVR,c语言,单片机)