MODBUS学习篇三

六.modbus实现主机对从设备读取数据包和从设备写入数据包
     1.准备好硬件上能精确到1ms的定时器
     2. 先完成一个MODBUS所需要的软件上的定时器

void Timer2_Init(void)                   //1ms产生一次更新事件
{
  TIM_TimeBaseInitTypeDef  TIM_TimerBaseInitStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	TIM_DeInit(TIM2);                                        //TIMx寄存器重设为缺省值
	TIM_TimerBaseInitStructure.TIM_Period = 1000 - 1;        //1ms
	TIM_TimerBaseInitStructure.TIM_Prescaler = 72 - 1;       //72 / 72 = 1MHz --> 1us
	TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2,&TIM_TimerBaseInitStructure);
	
	TIM_Cmd(TIM2,ENABLE);
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
}
void TIM2_IRQHandler()  //定时器2的中断服务子程序    1ms一次中断
{
   u8 st;
	 st = TIM_GetFlagStatus(TIM2, TIM_FLAG_Update);
	 if(st==SET)
	 {
	   TIM_ClearFlag(TIM2,TIM_FLAG_Update);
		 if(modbus.timrun!=0)
		 {
		   modbus.timout++;
			 if(modbus.timout>=8)  //间隔时间达到了时间
			 {
			   modbus.timrun=0;    //关闭定时器--停止定时器
				 modbus.reflag=1;    //收到一帧数据
			 }
		 }
	 }
}

烧录程序进行仿真,得到每1ms,定时器工作一次

    3.    (1)主函数进来后,
          (2)把定时器准备好,
          (3)中断准备好,
          (4)然后调用485的初始化函数

     LED_Init();
     Timer2_Init();
	 Modbus_Init();
	 Modbus_NVIC_Init();
   while(1)
	 {
		 Modbus_Event();    //处理modbus数据
	 }		

[初始化485的收发控制脚(配置成一个零电平),  初始化485所要用到的串口(配置串口),  初始化了这个串口的一些属性,而且允许这个串口接收数据触发中断,然后在通过这个中断初始化函数,激活2号串口的中断函数,把他他的优先级设置的高一点,定时器的优先级滞后一点,这样串口就有了接收和发送的能力]

void RS485_Init(void)
{
  USART_InitTypeDef  USART_InitStructure;
	GPIO_InitTypeDef   GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
	
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
	RS485_RT_0;  //使MAX485芯片处于接收状态
	
	//USART1_TX  PB.10
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//USART1_RX  PB.11
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//Usart1 NVIC
	USART_DeInit(USART2);
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl =   USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	
	USART_DeInit(USART2);       //USART2寄存器重设为缺省值    //使代码更健壮
	USART_Init(USART2, &USART_InitStructure);
	USART_ITConfig(USART2, USART_IT_RXNE,ENABLE);
	USART_Cmd(USART2,ENABLE);
	USART_ClearFlag(USART2,USART_FLAG_TC);
	
}

把他的优先级设置的高一点,定时器的优先级滞后一点

void  Modbus_NVIC_Init()
{
	//中断优先级NVIC设置
  NVIC_InitTypeDef   NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;             //TIM3中断
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //IRQ通道被使能
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   //先占优先级1级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;          //从优先级2级
  NVIC_Init(&NVIC_InitStructure);	                            //初始化NVIC寄存器
	
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;           //使能串口2中断
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能外部中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   //先占优先级1级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          //从优先级2级
	NVIC_Init(&NVIC_InitStructure);                             //根据NVIC_InitStructure中指定的参数初始化外设NVIC寄存器
}

串口就有了接收和发送的能力

void USART2_IRQHandler()    //MODBUS字节接收中断
{
   u8 st,sbuf;
	 st=USART_GetFlagStatus(USART2,USART_IT_RXNE);
	 if(st==SET)
	 {
	   sbuf = USART2->DR;
		 if(modbus.reflag==1) //有数据包正在处理
		 {
		   return ;
		 }
		 modbus.rcbuf[modbus.recount++]=sbuf;
		 modbus.timout=0;
		 if(modbus.recount==1) //收到主机发来的一帧数据的第一个字节
		 {
		    modbus.timrun=1;  //启动定时
		 }
	 }
}

测试串口的收发能力

void RS485_byte(u8 d)  //485发送一个字节
{
    RS485_RT_1;
	USART_SendData(USART2, d);
	while(USART_GetFlagStatus(USART2,USART_FLAG_TC) == RESET);
	USART_ClearFlag(USART2,USART_FLAG_TC);
	
	RS485_RT_0;
}

 

 

到此准备工作完成
加入CRC校验
**

 - 现在实现modbus协议

**
主要实现modbus的数据接收功能
首先在头文件中定义modbus的结构体,和类型

因为波特率  9600
1位数据的时间为   10000000us / 9600bit / s = 104us
一个字节为 104us*10位 = 1040us
所以 MODBUS确定一个数据帧完成的时间为    1040us*3.5 = 3.64ms   ->10ms
 

typedef struct
{
  u8 myadd;       //本机设备地址
	u8 rcbuf[100];  //MODBUS接收缓冲区
  u16 timout;     //MODBUS的数据断续时间
  u8 recount;     //MODBUS端口已经收到的数据个数
  u8 timrun;      //MODBUS定时器是否计时的标志
  u8 reflag;      //收到一帧数据的标志
  u8 Sendbuf[100];//MODBUS发送缓冲区	
}MODBUS;
extern MODBUS modbus;
```

在编写modbus初始化函数,在这里485的初始化函数要归属于modbus的初始化(485就是modbus的一个载体)

void Modbus_Init(void)
{
   modbus.myadd = 4;    //本从设备地址
	 modbus.timrun = 0;   //modbus定时器停止计时
	 RS485_Init();
}

测试modbus的收发

 

 在这里modbus已经收到数据帧,接下来就要对收到的数据帧做处理

void Modbus_Event(void)
{
  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;
	
}
 

 参考文件

 modbus工程文件

菱FX-通信-Modbus使用手册 Modbus是一种通信协议,用于在不同设备之间进行数据传输和通信。菱FX系列PLC(可编程逻辑控制器)也支持Modbus协议,通过这个手册,您将能够了解如何在FX PLC之间使用Modbus进行通信。 1. Modbus基础知识:首先,了解Modbus的基本概念和术语是非常重要的。该手册将介绍Modbus的通信方式,包括Modbus传输模式(RTU或ASCII)、寄存器、数据类型等。 2. Modbus通信设置:为了实现FX PLC之间的通信,您需要配置正确的Modbus参数。手册将详细介绍如何设置FX PLC的Modbus通信参数,包括端口、波特率等。 3. Modbus主从通信:FX PLC支持Modbus的主从通信模式,即一个FX PLC可以作为主站,控制其他设备(从站)的数据读取或写入操作。手册将详细介绍如何配置FX PLC作为Modbus主站以及如何与从站进行通信。 4. Modbus寄存器映射:在Modbus通信中,寄存器是用来存储和传输数据的重要组成部分。手册将介绍如何在FX PLC中设置和管理Modbus寄存器映射,以便数据正确地被读取或写入。 5. 示例程序和案例:为了帮助您更好地理解和应用Modbus通信,手册还提供了一些示例程序和实际应用案例,以便您可以根据实际情况进行定制和操作。 通过学习和使用菱FX用户手册-通信Modbus,您将能够深入理解和掌握在FX PLC之间进行Modbus通信的方法和技巧,从而更好地应用于您的工程项目中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值