STM32 Modbus通信学习笔记—— 代码及示例

文章目录

  • 前言
  • 从机帧格式
    • 举个栗子:
  • STM32 Modbus_RTU与维控屏通信
    • STM32代码
      • 1.定时器
      • 2串口收发
      • 3.数据包处理
  • 维控屏代码
  • 总结
  • 合集

前言

前面已经介绍了Modbus的通信流程以及主机的数据帧,此篇主要结合STM32的代码来进一步介绍从机端的帧格式以及整个通信过程。

从机帧格式

从机格式与上一篇的主机格式类似,从机会根据主机的命令和功能码返回对应信息,这里从机返回的地址、功能码是和主机发送的数据一致,数据段则是根据主机访问的范围而定。
STM32 Modbus通信学习笔记—— 代码及示例_第1张图片
RTU格式从机返回的帧为地址、功能码、总字节数、数据段、CRC校验码。
ASCLL的格式类似,在此不做赘述。

举个栗子:

这里以读取数据也就是功能码是03为例,其余功能码也是大同小异,想了解的可以私聊笔者获取代码。
STM32 Modbus通信学习笔记—— 代码及示例_第2张图片
如上图所示:主机发送的数据包为“04 03 00 00 00 09 85 99”
04是从设备地址;03是功能码,对应的功能是读取16位寄存器的值;
00 00 是起始寄存器位置,00 09是访问的个数。
85 99 是CRC校验码。
STM32 Modbus通信学习笔记—— 代码及示例_第3张图片
当从机4接收到这个指令后,就会解析出命令,并返回数据包:
“04 03 12 00 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 31 16 ”
04:从机地址;03功能码;0x12:字节数;00 00-----00 08是数据段,分别对应九个16位寄存器的高低八位。
31 16是CRC校验码。
STM32 Modbus通信学习笔记—— 代码及示例_第4张图片
STM32 Modbus通信学习笔记—— 代码及示例_第5张图片
查看代码中寄存器值得定义,可以发现与返回值一致。

STM32 Modbus_RTU与维控屏通信

了解完整个代码的主从帧和通信模式,接下来来试试应用;本示例以STM32和PLC常用的维控屏进行通信,主要实现
1.屏幕控制控件LED;

2.屏幕监控STM32LED灯状态;

3.屏幕监控按键的按下,并记录日志;

4.屏幕实时显示数字变量。

STM32代码

1.定时器

由于使用的是RTU的通信帧,在上一篇的介绍中可以知道RTU格式没有固定的包头和包尾,是通过3.5个字符的时间来作为包尾的,所以在为了实现正常通信,需要设置一个能够计时毫秒的定时器,根据然后根据自己的波特率进行计算,算出所需要的延时时间。例如使用9600时,就需要定时大于4ms时间,为了保证通信正常往往会留有余量,如下图就是使用的8ms来作为标志。
STM32 Modbus通信学习笔记—— 代码及示例_第6张图片

2串口收发

在完成定时器的初始化后,还需要能够保证串口的正常收发,笔者这里用的是STM32最小系统,自带CH340,维控屏也是使用的的电脑进行仿真,所以不需要针对类似485这样的硬件电路来对代码进行调整,只需要正常初始化串口就行。整体的代码框架如下:

// An highlighted block
/******************************************************************************
* 函数名		:main
* 描述			:主函数
* 参数			:无
* 返回			:无
* 编写者		:
* 编写日期	:
*******************************************************************************/
int main(void)
{	
	SystemInit();
	delay_init(72);
	CLI();                  		//关总中断
	NVIC_Configuration();
	General_Gpio_Init();				//通用GPIO口初始化
	GeneralIOStructInit();   //初始化IO口结构体变量	
	Usart1_Init(115200);			//串口1初始化
	
	SEI();                  		//开总中断
	IWDG_Init(4,6250); 
	user_printf(USART1,"初始化完毕\r\n");
	delay_ms(1000);
	delay_ms(1000);
	
	while(1)
	{
			com1_rtu_slave();//modbus处理函数
			AllModuleProDeal(); //所有运行代码
			General_Gpio_Update();		//通用GPIO口状态更新	
			heartbeat(); //心跳检测
			IWDG_Feed();//喂狗
	}
}

3.数据包处理

modbus从机的处理函数如下(注意此代码是灵育教育的modbus处理并非笔者上述工程所示的处理函数,但是总体思路一致,这里给大家作参考):

// An highlighted block
void Mosbus_Event()
{
	u16 crc;
	u16 rccrc;
  if(modbus.reflag==0)  //没有收到MODbus的数据包
	{
	  return ;
	}
	
	crc= crc16(&modbus.rcbuf[0], modbus.recount-2);       //计算校验码
  rccrc=modbus.rcbuf[modbus.recount-2]*256 + modbus.rcbuf[modbus.recount-1];  //收到的校验码
  if(crc ==  rccrc)  //数据包符号CRC校验规则
	{ 
	  if(modbus.rcbuf[0] == modbus.myadd)  //确认数据包是否是发给本设备的 
		{
		  switch(modbus.rcbuf[1])  //分析功能码
			{
			  case 0:     break;
			  case 1:     break;
		    case 2:     break;
		    case 3:     Modbud_fun3();    break;   //3号功能码处理
		    case 4:     break;
		    case 5:     break;
		    case 6:     Modbud_fun6();     break;   //6号功能码处理
	      case 7:     break;			
        //....				
			}
		}
		else if(modbus.rcbuf[0] == 0)   //广播地址
		{
		
		}
	}
	
	modbus.recount=0;   //
  modbus.reflag=0;	
}


void Modbud_fun3()  //3号功能码处理  ---主机要读取本从机的寄存器
{
  u16 Regadd;
	u16 Reglen;
	u16 byte;
	u16 i,j;
	u16 crc;
	Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3];  //得到要读取的寄存器的首地址
	Reglen=modbus.rcbuf[4]*256+modbus.rcbuf[5];  //得到要读取的寄存器的数量
	i=0;
	
	modbus.Sendbuf[i++]=modbus.myadd;//本设备地址
  modbus.Sendbuf[i++]=0x03;        //功能码      
  byte=Reglen*2;   //要返回的数据字节数
//modbus.Sendbuf[i++]=byte/256;  //
	modbus.Sendbuf[i++]=byte%256;
	
	for(j=0;j<Reglen;j++)
	{
	  modbus.Sendbuf[i++]=Reg[Regadd+j]/256;
		modbus.Sendbuf[i++]=Reg[Regadd+j]%256;		
	}
	crc=crc16(modbus.Sendbuf,i);
	modbus.Sendbuf[i++]=crc/256;  //
	modbus.Sendbuf[i++]=crc%256;
	
	RS485_RT_1;  //
	
	for(j=0;j<i;j++)
	{
	 RS485_byte(modbus.Sendbuf[j]);
	}
	
	RS485_RT_0;  //
}

void Modbud_fun6()  //6号功能码处理
{
  u16 Regadd;
	u16 val;
	u16 i,crc,j;
	i=0;
  Regadd=modbus.rcbuf[2]*256+modbus.rcbuf[3];  //得到要修改的地址 
	val=modbus.rcbuf[4]*256+modbus.rcbuf[5];     //修改后的值
	Reg[Regadd]=val;  //修改本设备相应的寄存器
	
	//以下为回应主机
	
	modbus.Sendbuf[i++]=modbus.myadd;//本设备地址
  modbus.Sendbuf[i++]=0x06;        //功能码 
  modbus.Sendbuf[i++]=Regadd/256;
	modbus.Sendbuf[i++]=Regadd%256;
	modbus.Sendbuf[i++]=val/256;
	modbus.Sendbuf[i++]=val%256;
	 crc=crc16(modbus.Sendbuf,i);
	 modbus.Sendbuf[i++]=crc/256;  //
	 modbus.Sendbuf[i++]=crc%256;
	
	RS485_RT_1;  //
	
	for(j=0;j<i;j++)
	{
	 RS485_byte(modbus.Sendbuf[j]);
	}
	
	RS485_RT_0;  //
}

维控屏代码

维控屏这边就是拖动控件进行布局,然后绑定寄存器地址,根据自己的需求做一些界面即可。
STM32 Modbus通信学习笔记—— 代码及示例_第7张图片

总结

有关modbus通信的介绍就到此为止,如有不足之处欢迎指出,相关工程也会放到笔者的下载资源,有需要的自行下载。

合集

STM32 Modbus通信学习笔记——理论基础
STM32 Modbus通信学习笔记——通信流程
STM32 Modbus通信学习笔记—— 代码及示例
STM32 使用MODBUS与维控屏通信(modbus系列代码)

你可能感兴趣的:(STM32,c语言,stm32,单片机,modbus,维控屏)