1 / 32

MODBUS RTU 主从通讯实验

MODBUS RTU 主从通讯实验. 1 .实验目的 学会使用 ZLG/Modbus RTU 软件包开发 Modbus 主机设备及从机设备。. 2 .实验设备. 硬件: PC 机 2 台 MagicARM2410 教学实验开发平台 2 台 RS485 通讯连接线 1 条 LA1032 逻辑分析仪 1 套 软件: Windows98/XP/2000 操作系统 ADS1.2 集成开发环境 μC/OS-II 操作系统( V2.52 ) ZLG/Modbus RTU 软件包. 3 .实验内容.

Télécharger la présentation

MODBUS RTU 主从通讯实验

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. MODBUS RTU主从通讯实验 1.实验目的 学会使用ZLG/Modbus RTU软件包开发Modbus主机设备及从机设备。

  2. 2.实验设备 • 硬件: PC机 2台 • MagicARM2410教学实验开发平台 2台 • RS485通讯连接线 1条 • LA1032逻辑分析仪 1套 • 软件:Windows98/XP/2000操作系统 • ADS1.2集成开发环境 • μC/OS-II操作系统(V2.52) • ZLG/Modbus RTU软件包

  3. 3.实验内容 (1) 编与ZLG/Modbus RTU软件包的底层接口函数。 (2) Modbus从机使用蜂鸣器B1模拟为1个线圈量,地址为0。 (3) Modbus主机通过检测KEY1按键控制从机蜂鸣器B1蜂鸣。

  4. 4.实验预习要求 (1) 仔细阅读产品配套光盘上该实验例程的目录中ZLG/Modbus RTU使用说明文档。 (2) 仔细阅参考文献[2]关于S3C2410A的UART模块和定时器模块的说明。

  5. 5.实验原理 本实验中使用到的软件包的API函数及资源占用的情况如下: ZLG/Modbus RTU软件包 API函数:主机/从机初始化函数、主机/从机 服务函数、主机功能代码执行函数。 外设资源占用:UART1和一个GPIO引脚GPE13 ,定 时器2、定时器3和定时器4。 μC/OS-II资源占用:Modbus主机使用3个事件,从 机使用1个事件;Modbus主机/从机服务任 务堆栈推荐使用64。

  6. 5.实验原理 在从机中,设定从机地址为1,并且使用蜂鸣器B1模拟为1个线圈量,其地址为0,主机通过写线圈操作改变蜂鸣器B1状态。当接收到写线圈地址0的值为1时,蜂鸣器B1 蜂鸣,反之蜂鸣器B1不响。 主机通过中使用1个任务,检测KEY1的状态,当KEY1按下时,发送写地址为0的从机0地址线圈的值为1,当KEY1松开时,发送写地址为0的从机线圈0地址的值为0。

  7. 6.实验步骤 主机部分: (1) 选用ARM Executable Image for DeviceARM2410 (μCOSII)工程模板创建MBMaster工程,并将μC/OS-II相关代码文件、Modbus主机相关文件复制到相关工程目录,然后分别添加MBCommon.c、MB_RTU.c、MBMaster.c、MB_MFunction.c和OSFUNfile.c到工程。 (2) 打开cofig.h文件,添加ZLG/Modbus主机相关的头文件MBMaster.h,由于在μC/OS-II操作系统中使用,所以还需要定义UCOSII宏,如程序清单 1.1所示。

  8. 6.实验步骤 程序清单 1.1 添加头文件 /********************************** ** ZLG/Modbus TRU相关头文件与配置 ***********************************/ #define UCOSII // 在uC/OS-II下使用本必须定义该宏 #include "MBMaster.h" // Modbus主机栈头文件 void IniUART1(uint32 bps); // Modbus使用的串行口 void UART1_Exception(void); // Modbus使用串行口的中断服务函数 void TimersInit(void); // Modbus使用定时器初始化 void T15_Exception(void); // Modbus T15定时器中断服务函数 void T35_Exception(void); // Modbus T35定时器中断服务函数 void T10ms_Exception(void); // Modbus T10MS定时器中断服务函数

  9. 6.实验步骤 (3) 编写ZLG/Modbus RTU主机软件包接口文件SYSHAL.C。主要是编写:void SendResponse(uint8 *buff,uint16 len):发送应答帧函数;void StartCountT15(void):T1.5开始计时函数;void StartCountT35(void):T3.5开始计时函数;void Start10mS(void):10mS开始计时函数。 此外还有与系统相关的UART1初始化、定时器2、定时器3和定时器4初始化以及相应中断的异常处理函数。(注意:定时器4需要自动重加载定时值。)

  10. 6.实验步骤 (4) 删除模板的默认任务,UART0初始化和LCD初始化的相关代码。 (5) 在target.c文件void VICInit(void)函数中添加定时器2、定时器3、定时器4及UART1中断向量初始化代码。如程序清单 1.2所示。 程序清单 1.2 Modbus使用的中断配置 VICVectAddr[12] = (uint32) T15_Exception; rINTMSK &= ~(1<<12); // 打开TIMER2中断允许 VICVectAddr[13] = (uint32) T35_Exception; rINTMSK &= ~(1<<13); // 打开TIMER3中断允许 VICVectAddr[14] = (uint32) T10ms_Exception; rINTMSK &= ~(1<<14); // 打开TIMER4中断允许 VICVectAddr[23] = (uint32) UART1_Exception; rSUBSRCPND =(BIT_SUB_RXD1|BIT_SUB_ERR1); rINTSUBMSK &=~(BIT_SUB_RXD1|BIT_SUB_ERR1); ClearPending(BIT_UART1); rINTMSK &=~(BIT_UART1);

  11. 6.实验步骤 同时也在target.c文件中void TargetInit(void)中初始化定时器2、定时器3、定时器4和UART1的波特率为115200及初始化HostMassLib,如程序清单 1.3所示。 程序清单 1.3 初始化UART0波特率 void TargetInit(void) { uint32 temp; OS_ENTER_CRITICAL(); srand((uint32) TargetInit); VICInit(); Timer0Init(); IniUART1(115200); // Modbus驱动使用的波特率 TimersInit(); // 定时器初始化 Start10mS(); // 起动10mS定时计数 MBMasterIni(); // Modbus主机初始化函数 ... }

  12. 6.实验步骤 (6) 在main.c文件中编写Modbus主机实验程序(如程序清单 1.4所示),并选择Debug生成目标,然后编译连接工程。 注意:由于Modbus主机软件包使用了3个事件,所以需要在os_cfg.h文件中修改适当的事件数。 程序清单 1.4 Modbus主机实验程序 #define Task0StkLengh 64 // 定义用户任务0的堆栈长度 OS_STK Task0Stk [Task0StkLengh]; // 定义用户任务0的堆栈 void Task0(void *pdata); // Task0 任务0 #define OSModbusServeStkLengh 64 OS_STK OSModbusServeStk [OSModbusServeStkLengh]; int main (void) { OSInit (); OSTaskCreate ( Task0,(void *)0, &Task0Stk[Task0StkLengh - 1], 2 ); OSTaskCreate ( OSModbusServe,(void *)0, &OSModbusServeStk[OSModbusServeStkLengh - 1], 3 ); OSStart (); return 0; }

  13. 6.实验步骤 #define KEY1 (1<<4) // rGPFCON[9:8] = 00b,设置GPF4为GPIO输入模式 #define key1_ini() rGPFCON = (rGPFCON & (~(0x03<<8))); #define key1_get() (rGPFDAT & KEY1) #define key2_ini() PINSEL1 &= ~(3<<(KEY2-16)*2); IO0DIR &= ~(1<<KEY2) #define key2_get() (IO0PIN &(1<<KEY2)) void Task0 (void *pdata) { pdata = pdata; TargetInit (); key1_ini(); while (1) { if( 0 == key1_get() ) // 有按键按下 { // 写设备地址为1的线圈地址为1的值为1 OSWriteSingleCoil( 0x01,1,COIL_ON ); } else { // 写设备地址为1的线圈地址为1的值为0 OSWriteSingleCoil( 0x01,1,COIL_OFF ); } } }

  14. 6.实验步骤 从机部分: (7) 选用ARM Executable Image for DeviceARM2410 (μCOSII)工程模板创建MBSlave工程,并将μC/OS-II相关代码文件、Modbus从机相关文件包拷贝到相关工程目录,然后分别添加MBCommon.c、MB_RTU.c、MBSlave.c和MB_Pfunction.c文件到工程。 (8) 打开config.h文件,添加ZLG/Modbus主机相关的头文件MBMaster.h,由于在μC/OS-II操作系统中使用,所以还需要定义UCOSII宏,如程序清单 1.5所示。 程序清单 1.5 修改config.h文件 #define UCOSII // 在uC/OS-II下使用本必须定义该宏 #include "MBSlave.h" // Modbus从机栈头文件 void IniUART1(uint32 bps); // Modbus使用的串行口 void UART1_Exception(void); // Modbus使用串行口的中断服务函数 void TimersInit(void); // Modbus使用定时器初始化 void T15_Exception(void); // Modbus T15定时器中断服务函数 void T35_Exception(void); // Modbus T35定时器中断服务函数

  15. 6.实验步骤 (9) 编写ZLG/Modbus RTU主机软件包接口文件SYSHAL.C,与主机使用相同的代码。 (10) 在IRQ.s文件中添加定时器1及UART0中断句柄,参见实验步骤(4)。 (11) 在target.c文件void VICInit(void)函数中添加定时器1及UART0中断向量初始化代码,参见实验步骤(5)。 (12) 在MBSlave.h文件中配置使用写单线圈Modbus功能代码,并配置线圈的个数为1,如程序清单 1.6所示。

  16. 6.实验步骤 程序清单 1.6 设置Modbus从机功能代码 /*************************************************** ** 使能Modbus功能代码 ****************************************************/ #define READ_COILS_EN 0 // 读线圈 #define READ_DIS_INPUT_EN 0 // 读离散量输入 #define READ_HOLD_REG_EN 0 // 读保持寄存器 #define READ_INPUT_REG_EN 0 // 读输入寄存器 #define WRITE_SING_COIL_EN 1 // 写单个线圈 #define WRITE_SING_REG_EN 0 // 写单个寄存器 #define WRITE_MULT_COIL_EN 0 // 写多个线圈 #define WRITE_MULT_REG_EN 0 // 写多个寄存器 #define MASK_WRITE_REG_EN 0 // 处理屏蔽寄存器指令 #define READ_WRITE_REG_EN 0 // 读写多个寄存器 /*************************************************** ** 配置线圈的参数。定义线圈的数量 ****************************************************/ #define END_COILS_ADDR 1

  17. 6.实验步骤 (13) 编写与Modbus从机功能代码相关的接口函数,当写线圈地址0的值为1时,蜂鸣器蜂鸣,写线圈地址0的值为0时,蜂鸣器熄灭,如程序清单 1.7所示。 程序清单 1.7 从机功能代码相关的接口函数 #define BEEP (1<<10) // 定义蜂鸣器控制口 #define BEEP_MASK (~BEEP) // 蜂鸣器B1 GPIO初始化 #define IniBUZZ() rGPHCON = (rGPHCON & (~(0x03<<20))) | (0x01<<20); #define SetBUZZ() rGPHDAT = rGPHDAT | BEEP // 蜂鸣器蜂鸣 #define ClrBUZZ() rGPHDAT = rGPHDAT & BEEP_MASK // 蜂鸣器关闭 // 输入参数:Address,线圈地址; CoilValue,线圈值(0\1) // 输出参数:返回寄存器值 // 功能描述:设置线圈值函数,访函数由用户编写 uint8 MB_SetCoil(uint16 Address,uint8 CoilValue) { if(Address==0) // 地址为1 { if( CoilValue == 1 ) ClrBUZZ(); // 蜂鸣器蜂鸣 else SetBUZZ(); // 蜂鸣器关闭 } return TRUE; }

  18. 6.实验步骤 (14) 在main.c文件中编写Modbus从机实验程序(如程序清单 1.8所示),并选择Debug生成目标,然后编译连接工程。 注意:由于Modbus主机软件包使用了1个事件,所以需要在os_cfg.h文件中修改适当的事件数。 程序清单 1.8 Modbus从机实验程序 #define Task0StkLengh 64 // 定义用户任务0的堆栈长度 OS_STK Task0Stk [Task0StkLengh]; // 定义用户任务0的堆栈 void Task0(void *pdata); // Task0 任务0 #define TaskModBUSStkLengh 64 OS_STK TaskModBUSStk [TaskModBUSStkLengh]; int main (void) { OSInit (); OSTaskCreate ( Task0,(void *)0, &Task0Stk[Task0StkLengh - 1], 2); OSTaskCreate ( OSModbusServe,(void *)0, &TaskModBUSStk[TaskModBUSStkLengh - 1], 4); OSStart (); return 0; }

  19. 6.实验步骤 /***************************************************************************/ /* 设定Modbus从机信息 */ /***************************************************************************/ uint8 ADUBuff[ MAX_ADU_LENGTH ]; // ADU数据缓冲区 SLAVE_INFORMATION SlaveDevice = { 0x01, // 该Modbus从机地址 0x00, // 链路层协议 1152, // 波特率 = BaudRate * 100 0, // 奇偶校验 2, // 停止位 ADUBuff // 主机请求从帧指针 }; void Task0 (void *pdata) { pdata = pdata; TargetInit (); IniBUZZ(); while (1) { OSTimeDly(OS_TICKS_PER_SEC); } }

  20. 6.实验步骤 联机通讯部分: (15) 使用短路器分别短接主机和从机的JP2跳线器选择485R,并且短接Modbus从机的JP9。 (16) 使用电缆分别将主机J3的485A、485B和从机J3的485A、485B连接。 (17) 启动AXD分别全速运行Modbus主机和从机实验程序。 (18) 按下Modbus主机的KEY1按键时,Modbus从机的蜂鸣器蜂鸣,松开按键时,Modbus从机的蜂鸣器熄灭。 (19) 使用LA1032逻辑分析仪的Modbus插件观察Modbus主机发送的请求包和Modbus主机从机的应答包。主机的KEY1按下时,Modbus从机接收到请求帧如图 1.1所示,Modbus从机回应的应答帧如图 1.2所示。

  21. 6.实验步骤 联机通讯部分: (15) 使用短路器分别短接主机和从机的JP2跳线器选择485R,并且短接Modbus从机的JP9。 (16) 使用电缆分别将主机J3的485A、485B和从机J3的485A、485B连接。 (17) 启动AXD分别全速运行Modbus主机和从机实验程序。 (18) 按下Modbus主机的KEY1按键时,Modbus从机的蜂鸣器蜂鸣,松开按键时,Modbus从机的蜂鸣器熄灭。 (19) 使用LA1032逻辑分析仪的Modbus插件观察Modbus主机发送的请求包和Modbus主机从机的应答包。主机的KEY1按下时,Modbus从机接收到请求帧如图 1.1所示,Modbus从机回应的应答帧如图 1.2所示。

  22. 6.实验步骤 图 1.1 按下按键时Modbus从机接收到请求帧 图 1.2 按下按键时Modbus从机回应的应答帧

  23. 6.实验步骤 松开按键时,Modbus从机接收到请求帧如图 1.3所示,Modbus从机回应的应答帧如图 1.4所示。 图 1.3 按下按键时Modbus从机接收到请求帧 图 1.4 按下按键时Modbus从机回应的应答帧

  24. 7.实验参考程序 发送应答帧函数SendResponse,如程序清单1.9所示。 程序清单1.9 SendResponse void SendResponse(uint8 *buff,uint16 len) { uint16 i,k; RS_485_S(); // RS485发送使能 for(k=0;k<len;k++) { while(!(rUTRSTAT1 & 0x02)); // 等待发送器THR为空 for(i=0; i<10; i++); rUTXH1 = buff[k]; // 发送数据 while(!(rUTRSTAT1 & 0x02)); // 等待发送器THR为空 } while(!(rUTRSTAT1 & 0x04)); // 等待发送器THR为空 RS_485_R(); // RS485接收使能 }

  25. 7.实验参考程序 T1.5开始计时函数StartCountT15,如程序清单1.10所示。 程序清单1.10 StartCountT15 void StartCountT15(void) { uint32 temp = rTCON& TIMERS_UP_MAK; rTCNTB2 = 75; // 定时750 us rTCON = temp|(1<<13); // 更新定时器数据 rTCON = temp|(1<<12); // 启动定时器 } T3.5开始计时函数StartCountT35,如程序清单1.11所示。 程序清单1.11 StartCountT35 void StartCountT35(void) { uint32 temp = rTCON & TIMERS_UP_MAK; rTCNTB3 = 750; // 定时1750 us rTCON = temp|(1<<17); // 更新定时器数据 rTCON = temp|(1<<16); // 启动定时器 }

  26. 7.实验参考程序 10mS开始计时函数Start10mS,如程序清单1.12所示。 程序清单1.12 Start10mS void Start10mS(void) { uint32 temp = rTCON& TIMERS_UP_MAK; rTCNTB4 = 1000; // 定时10000 us rTCON = temp|(1<<21); // 更新定时器数据 rTCON = temp|(1<<20)|(1<<22); // 启动定时器 } UART1初始化函数IniUART1,如程序清单1.13所示。 程序清单1.13 UART1初始化函数 #define RS_485_S_R 13 // 485发送与接收控制引脚,初始化GPIO #define RS_485_INI() rGPECON = (rGPECON & (~(0x03<<26))) | (0x01<<26); #define RS_485_S() rGPEDAT |= (1<<RS_485_S_R); // 485发送使能 #define RS_485_R() rGPEDAT &= ~(1<<RS_485_S_R); // 485接收使能

  27. 7.实验参考程序 void IniUART1(uint32 bps) { RS_485_INI(); // IO口设置 (GPH5,GPH4) rGPHUP = rGPHUP | (0x03<<4); rGPHCON = (rGPHCON & (~0x00000F00)) | (0x00000A00); // 串口模式设置 rUFCON1 = 0x00; // 禁止FIFO功能 rUMCON1 = 0x00; // AFC(流控制)禁能 rULCON1 = 0x07; // 禁止IRDA,无奇偶校验,2位停止位,8位数据位 rUCON1 = 0x105; // 使用PCLK来生成波特率,发送中断为电平触发模 // 式,接收中断为边沿触发模式,禁止接收超时中 // 断,使能接收错误中断,正常工作模式, // 中断或查询方式(非DMA) rUBRDIV1=(int)(PCLK/16.0/bps + 0.5) -1; // 串口波特率设置 }

  28. 7.实验参考程序 定时器2、定时器3和定时器4初始化函数TimersInit,如程序清单1.14所示。 程序清单1.14 TimersInit void TimersInit(void) { // Fclk=200MHz,Pclk=50MHz。 定时器的记数单位为1微秒 rTCFG0 &=~(0xff<<8); rTCFG0 |= 250<<8; // 预分频器0设置为250,取得200KHz rTCFG1 &= ~(0xf<<8); // TIMER2再取1/2分频,取得100KHz rTCFG1 &= ~(0xf<<12); // TIMER3再取1/2分频,取得100KHz rTCFG1 &= ~(0xf<<16); // TIMER4再取1/2分频,取得100KHz rTCON &= ~(0x7ff<<12); // 初始化定时器2、3、4的控制位,全为零 }

  29. 7.实验参考程序 UART1中断处理函数UART1_Exception,如程序清单1.15所示。 程序清单1.15 UART1接收中断 void UART1_Exception(void) { rINTSUBMSK|=(BIT_SUB_RXD1|BIT_SUB_ERR1); if(rUTRSTAT1 & 0x01) { ReceOneChar(rURXH1); // 调用Modbus接收字符函数 } ClearPending(BIT_UART1); rSUBSRCPND =(BIT_SUB_RXD1|BIT_SUB_ERR1); rINTSUBMSK &=~(BIT_SUB_RXD1|BIT_SUB_ERR1); }

  30. 7.实验参考程序 定时器2中断处理函数T15_Exception,如程序清单1.16所示。 程序清单1.16 T15_Exception void T15_Exception(void) { rSRCPND = BIT_TIMER2; rINTPND = BIT_TIMER2; rINTPND; T15EndHandle(); rTCON &= ~(1<<12); // 关定时器 } 定时器3中断处理函数T35_Exception,如程序清单 1.17所示。 程序清单 1.17 T35_Exception void T35_Exception(void) { rSRCPND = BIT_TIMER3; rINTPND = BIT_TIMER3; rINTPND; T35EndHandle(); rTCON &= ~(1<<16); // 关定时器 }

  31. 7.实验参考程序 定时器4中断处理函数T10ms_Exception,如程序清单 1.18所示。 程序清单 1.18 T10ms_Exception void T10ms_Exception(void) { rSRCPND = BIT_TIMER4; rINTPND = BIT_TIMER4; rINTPND; Time10mSHandle(); }

  32. 8.思考题 请用户思考一下,如何使用Modbus RTU软件包设计一个设备与现有的Modbus设备通讯?

More Related