1、 测控网络课程设计 姓名: 班级: 学号: 同组者: 第一部分 试验要求 1.要求 此次实践以开发计算机测控系统为最终目标,要求掌握计算机测控系统工作原理,学习组态王工控组态软件使用方法,依据要求完成工程组态;掌握MODBUS通信协议原理,开发含有MODBUS通讯功效智能仪表,最终完成和组态工程之间通讯。 2.设计内容和时间计划 (1) 熟悉组态王软件安装,基础开发环境,采取构建简单工程(采取仿真数据和设备,工程应包含PID功效),计划时间1天; (2) 依据罐区工
2、艺要求,完成对应组态工程,实现对原油储罐监控,计划时间2天; (3) 掌握MODBUS通讯协议工作原理,在MSP430F5438单片机上编程实现MODBUS用户端服务程序,要求经过串行口将现场温度、泵状态、流量等参数上传到上位机上,计划时间3天。 (4) 在单片机上编程实现流量、温度上下限及仪表地址和波特率等参数设置功效,同时能从上位机对仪表参数进行设置,计划时间2天。 (5) 优化设计,要求当出现通讯错误时在上位机和单片机上全部要做出对应反应,计划时间0.5天。 (6) 上位机采取高级语言编程,实现对现场智能仪表控制。 第6项为有能力者完成,计划时间1.5天。 3.具体设计要求
3、 3.1 组态王工程部分:见附录1 3.2 智能仪表部分: (1)仪表支持MODBUS ASCII和MODBUS RTU通讯协议。 (2)仪表含有现场参数修改功效(变送器参数、泵参数和通讯参数能够自由设置,且单位能够更改),参数最好掉电不丢失。 (3)仪表含有参数上传功效,能够经过上位机实现对仪表参数设置。 (4)当上位机发送命令有问题时,仪表应进行错误处理;当仪表返回数据有错误时,上位机也应作出反应。 3.3 MODBUS上位机软件:能够正确读写仪表数据。 第二部分 试验内容 组态王部分 1、罐区工艺步骤图 2- 储油罐进口电动阀;3- 储油罐排污电动阀;4- 储油罐
4、出口电动阀;5- 泵 图1:罐区工艺步骤图 2、 监控要求 (1)监测各罐液位(0-20m)/ (0-1m)/温度(0-100度)(现场仪表4-20mA输出)。 (2)依据各罐液位控制各罐出口电动阀(H>16m, 关进口阀,选择最低液位罐进油;H<2m,关出口阀,选择最高液位罐出油),手动遥控排污阀。 液位H>15.5m高报警, H>17m高高报警; 液位H<2m低报警, H<1.5m低低报警。 界位>1m高报警, 界位>1.5m高高报警; 界位<0.5m低报警, H<0.2m低低报警; (3)开启泵组设置出入口流量(100M3/h,200M3/h,250M3/h)
5、 3、 硬件配置 泵 现场变送器 1、组态画面主画面 泵参数 2、 数据连接 数据词典 管道 液位 阀门 3、 曲线 4、 画面命令 泵输入总流量=泵1*1 + 泵2*2 + 泵3*2.5; 泵输出总流量=泵4*1 + 泵5*2 + 泵6*2.5; 系数1=0.25; 系数2=0.2; 系数3=0.15; 系数4=0.3; if(泵总开==1 && 泵输入总流量>0) { if(液位1<=液位2 && 液位1<=液位3 && 液位1<=液位4 && 液位1<16) { 进口阀1=1; 进口阀
6、2=0; 进口阀3=0; 进口阀4=0; 液位1=液位1+泵输入总流量*系数1; } else { if(液位2<液位1 && 液位2<=液位3 && 液位2<=液位4 && 液位2<16) { 进口阀1=0; 进口阀2=1; 进口阀3=0; 进口阀4=0; 液位2=液位2+系数2*泵输入总流量; } else { if(液位3<液位1 && 液位3<液位2 && 液位3<=液位4 && 液位3<16) { 进口阀1=0; 进口阀2=0; 进口阀3=1; 进口阀4=0; 液位3=液位3+系数3*泵输入总流量; } else
7、 { if(液位4<液位1 && 液位4<液位2 && 液位4<液位3 && 液位4<16) { 进口阀1=0; 进口阀2=0; 进口阀3=0; 进口阀4=1; 液位4=液位4+系数4*泵输入总流量; } } } } } if(泵总关==1 && 泵输出总流量>0) { if(液位1>=液位2 && 液位1>=液位3 && 液位1>=液位4 && 液位1>2) { 出口阀1=1; 出口阀2=0; 出口阀3=0; 出口阀4=0; 液位1=液位1-系数1*泵输出总流量; } else { if(液位2>=液位1 && 液位2>=液位3 && 液
8、位2>=液位4 && 液位2>2) { 出口阀1=0; 出口阀2=1; 出口阀3=0; 出口阀4=0; 液位2=液位2-系数2*泵输出总流量; } else { if(液位3>=液位1 && 液位3>=液位2 && 液位3>=液位4 && 液位3>2) { 出口阀1=0; 出口阀2=0; 出口阀3=1; 出口阀4=0; 液位3=液位3-系数3*泵输出总流量; } else { if(液位4>=液位1 && 液位4>=液位2 && 液位4>=液位3 && 液位4>2) { 出口阀1=0; 出口阀2=0; 出口阀3=0; 出口阀4=1; 液位4=
9、液位4-系数4*泵输出总流量; } } }}} if(排污阀1==1 || 排污阀2==1 || 排污阀3==1 || 排污阀4==1) { 液位1=液位1-排污阀1*0.1; 液位2=液位2-排污阀2*0.1; 液位3=液位3-排污阀3*0.1; 液位4=液位4-排污阀4*0.1; } if(液位1>=16) {进口阀1=0;} if(液位2>=16) {进口阀2=0;} if(液位3>=16) {进口阀3=0;} if(液位4>=16) {进口阀4=0;} if(液位1<=2) {出口阀1=0;} if(液位2<=2) {出口阀2=0;}
10、if(液位3<=2) {出口阀3=0;} if(液位4<=2) {出口阀4=0;} if(泵输入总流量==0) { 进口阀1=0; 进口阀2=0; 进口阀3=0; 进口阀4=0; } if(泵输出总流量==0) { 出口阀1=0; 出口阀2=0; 出口阀3=0; 出口阀4=0; } 智能仪表部分 Modbus 协议是应用于电子控制器上一个通用语言。经过此协议,控制器相互之间、控制器经由网络(比如以太网)和其它设备之间能够通信。此协议定义了一个控制器能认识使用消息结构,而不管它们是经过何种网络进行通信。它描述了控制器请求访问其它设备过程,假如回应来自其它设备
11、请求,和怎样侦测错误并统计。它制订了消息域格局和内容公共格式。 1、 变量定义及函数申明 unsigned char RX[32]; //单片机接收数据数组 unsigned char TX[32]; //03功效时单片机发送数据数组 unsigned char TX6[32]; //06功效时单片机发送数据数组 unsigned char Buf[10]; //存放数据数组 unsigned int flag; // 上位机发送数据标志位 unsigned int flag1; //单片机应答数据标志位 unsigned int flow; // 流量值 unsigned
12、int temperature; //温度值 unsigned char LRC; //上位机发送数据校验码 unsigned char LRCt; //单片机应答数据校验码 void TTXX(); void In_LRC(); void Out_LRC(); unsigned char shitohex(unsigned int shi) ; unsigned char asciitohex(unsigned char ascii) ; void selflow(unsigned char selflow) ; void seltemp(unsigned char
13、seltemp) ; unsigned char MODBUS_LRC(unsigned char *auchMsg, unsigned short usDataLen); unsigned char hextoascii(unsigned char hex); void RS232_init(); void RS232_open(); void RS232_close(); 2、 串口初始化 void RS232_init()// RS232 串口初始化 9600,N,8,1 UART2 { P9DIR = 0xFF; P9SEL = 0x30; // P9.4,5 =
14、USCI_A2TXD/RXD UCA2CTL1 |= UCSWRST; // **Put state machinein reset** UCA2CTL1 |= UCSSEL_2; // CLK = SMCLK UCA2BR0 = 78; // 9600 (see User'sGuide) UCA2BR1 = 0x00; // UCA2MCTL |= UCBRS_0+UCBRF_2+ UCOS16; // Modulation UCBRSx=6, UCBRFx=0 UCA2CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
15、 } // RS232 串口打开 UART2 void RS232_open() { UCA2IE |= UCRXIE; // Enable USCI_A2 RX interrupt } // RS232 串口关闭 UART2 void RS232_close() { UCA2IE &= ~UCRXIE; // Disable USCI_A2 RX interrupt } 3、 上位机利用UART中止给单片机发送指令 #pragma vector=USCI_A2_VECTOR __interrupt void USCI_A2_ISR(void) { w
16、hile (!(UCA2IFG & UCTXIFG)); // 判定是否发送完成 if(UCA2RXBUF == ':') //:为起始标志,假如开始,标志位flag置位 { flag = 1; tempnumb--; } if(flag==1) //当标志位flag置位说明发送命令开始,开始接收命令数据 { RX[0] = ':'; if(UCA2RXBUF != 0x0D && UCA2RXBUF !=
17、 0x0A ) //只要不是回车换行符,就依次将数据存入接收数组RX中,同时计数变量tempnumb加1 { tempnumb++; RX[tempnumb] = UCA2RXBUF ; } if(UCA2RXBUF == 0x0D) //若是回车 { tempnumb++; RX[tempnumb] = 'D' ; } if(UCA2RXBUF == 0x0A) //若是换行
18、 { tempnumb++; RX[tempnumb] = 'A' ; flag=0; //接收标志位清零 flag1=1; //发送标志位置1 TTXX(); //调用发送数据函数 } } } 4、 单片机经过UART中止向上位机发送应答指令 void TTXX() { int i; int j; unsigned int b; unsigned int
19、 c; unsigned int d; unsigned char temp; In_LRC(); //计数上位机发送命令校验码 if(RX[8]=='1') //假如地址为寄存器0001,发送流量数据 shitohex(flow); if(RX[8]=='2') //假如地址位寄存器0002,发送温度数据 shitohex(temperature); if(RX[3]=='0' && RX[4]=='3') //假如是03号功效 { if((LRCHi == RX[tempnumb-3]) &&(LRCLo ==
20、RX[tempnumb-2])) //假如上位机发送校验码正确
{
for(b=0;b<=4;b++)
{
TX[b] = RX[b];
}
temp = asciitohex(RX[tempnumb-4]); //计算单片机要发送数据字节数
TX[5] = hextoascii(((temp*2)>>4)&0x0F);
TX[6] = hextoascii((temp*2)&0x0F);
for(i=0;i 21、 TX[7+i] = hextoascii(Buf[i]); //要发送数据
TX[8+i] = hextoascii(Buf[i+1]);
}
tempnumt = 7+i; //统计数据长度,用于计算校验码
Out_LRC(); //计算单片机发送数据校验码
TX[7+i] = LRCHi;
TX[8+i] = LRCLo;
TX[9+i] = 0x0D;
TX[10+i] = 0x0A;
for(j=0;j<=(10+i);j++) //单片机发送数据
22、 {
while (!(UCA2IFG & UCTXIFG)); // 判定是否发送完成
UCA2TXBUF=TX[j];
}
}
else //假如校验码不正确,返回错误代码 Input LRC ERROR!只能经过串口调试看到
{
ERR[19]='8';
ERR[20]=TX[4];
for(d=0;d<=20;d++)
{
while (!(UCA2IFG & UCTXIFG)); 23、// 判定是否发送完成
UCA2TXBUF=ERR[d] ;
}
}
}
else if(RX[3]=='0' && RX[4]=='6') //假如是06号功效
{
for(c=0;c<=tempnumb;c++)
{
TX6[c] = RX[c];
while (!(UCA2IFG & UCTXIFG)); // 判定是否发送完成
UCA2TXBUF=TX6[c];
}
selflow(TX6[10]); 24、 //设置流量值
seltemp(TX6[11]); //设置温度值
}
flag1=0; //单片机发送标志位清零
tempnumb=0; //上位机发送数据计算值清零
}
5、 LRC校验模块
unsigned char MODBUS_LRC(unsigned char *auchMsg, unsigned short usDataLen)
{ unsigned char uchLRC = 0 ; // LRC 初始化
while (usDataLen--) // 完成整个报文缓冲区
uchLRC += *auchMsg++ ; 25、 //缓冲区字节相加,无进位
return ((unsigned char)(-((char)uchLRC))) ; // 返回二进制补码 } (2)发送和应答校验码计数程序
}
void In_LRC()
{
unsigned int a;
for(a=1;a<(tempnumb-3);a+=2)
{
tempRX[(a-1)/2]=(asciitohex(RX[a])<<4) | asciitohex(RX[a+1]);
}
LRC=MODBUS_LRC(&tempRX[0],(tempnumb-4)/2); //进行LRC 26、效验计算
LRCHi = hextoascii((LRC>>4)&0x0F);
LRCLo = hextoascii(LRC&0x0F);
}
void Out_LRC()
{
unsigned int c;
for(c=1;c 27、tempTX[0],(tempnumt-1)/2); //进行LRC效验计算
LRCtHi = hextoascii((LRCt>>4)&0x0F);
LRCtLo = hextoascii(LRCt&0x0F);
}
6、 进制转换
//十六进制数转换为ASCII码
unsigned char hextoascii(unsigned char hex)
{
if(hex<=0x09)
return hex+0x30;
else return hex+0x37;
}
//ASCII码转换为十六进制数
unsigned 28、char asciitohex(unsigned char ascii)
{
if(ascii<=0x39)
return ascii-0x30;
else return ascii-0x37;
}
//十进制转十六进制, 因为组态王通信时会自动将十六进制数转换位十进制数, 所以需要在 单片机内将十进制数转换位十六进制数,这么组态王读到就是十进制数据。
unsigned char shitohex(unsigned int shi)
{
int i,b,d,c;
int a=0;
d=4;
char shiliu[10];
29、while (shi) //shi代表对应十进制数
{ c=shi%16; //每次除以16取余求得对应十六进制数
shi=shi/16; shiliu[a] = c; a++;
}
for(i=a;i<=4;i++) //得到数据首位倒置,才是要求十六进制数
{ shiliu[i]=0; }
for(b=0;b<4;b++)
{ d--; Buf[b]=shiliu[d]; }
return 0;
}
7、 波特率、流量、温度设置
void selflow(unsigned char selflow) //选定流量
{ switch(selflow)
30、
{
case '0':flow=100; break;
case '1':flow=1799; break;
case '2':flow=5000; break;
default:flow=1799; break;
}
}
void seltemp(unsigned char seltemp) //选定温度
{
switch(seltemp)
{
case '0':temperature=1;break;
case '1':temperature=20; break;
case '2':temper 31、ature=100; break;
default:temperature=20; break;
}
}
void selbps(unsigned char selbps) //设定波特率
{
switch(selbps)
{
case '0': UCA1CTL1 |= UCSSEL_1; // 时钟源选择
UCA1BR0 = 0x1B; //1200
UCA1BR1 = 0x00; UCA1MCTL = 04; break;
case '1': UCA1CTL1 |= UCSSEL_1; // 时钟源选择
UC 32、A1BR0 = 0x0D; //2400
UCA1BR1 = 0; UCA1MCTL = 0X0A; break;
case '2': UCA1CTL1 |= UCSSEL_1; // 时钟源选择
UCA1BR0 = 6; //4800
UCA1BR1 = 0; UCA1MCTL = 0x0C; UCA1CTL1 &= ~UCSWRST; // 使能串口功效
UCA1IE |= UCRXIE; // 使能接收中止
_BIS_SR(GIE); break;
case '3': UCA1CTL1 |= UCSSEL_1; // 时钟源选择
33、
UCA1BR0 = 3; //9600
UCA1BR1 = 0; UCA1MCTL = 06; UCA1CTL1 &= ~UCSWRST; // 使能串口功效
UCA1IE |= UCRXIE; // 使能接收中止
_BIS_SR(GIE); break;
default:UCA1CTL1 |= UCSSEL_1; // 时钟源选择
UCA1BR0 = 3; // 32768hz/3=9600
UCA1BR1 = 0; UCA1MCTL = 06; break;
}
}
8、 主函数
void main(void)
{
34、 WDTCTL = WDTPW+WDTHOLD; //关闭看门狗
clock_init(); //时钟初始化
lcm_init(); //LCM初始化
RS232_init();
RS232_open();
_EINT(); //打开总中止
while(1)
{ }
}
总结
此次课程实习,关键对组态王学习有了更深认识,能够愈加好使用组态王软件,和了解她工作原理。经过不停学习和使用,也加强了我学习知识,查阅资料能 35、力。此次课程中有很多以前没有碰到过问题,比如画面命令编写,之前是经过PLC,而这次是直接用组态王模拟,需要定义时和命令中变量名称相同。编程思绪和方法和plc相同。经过学习,收益良多,而且这次接触了新内容,通讯协议。这部分内容较难了解,我们也认真努力去学习了解它,即使最终没能将通信部分弄出来,但其中收获是无价,再次感谢老师教导。
碰到问题:组态部分整体设计,管道参数设计,画面命令等部分细节方面问题。
处理方法:经过和同学交流探讨,设计整体画面。查阅资料,和老师交流,设置管道参数等等。比如设置管道参数,需要考虑管道何时流水,和泵关系,拿出进水泵为例,假如三个入水泵全部开才有水需用和命令,若三个进水泵有一个开有水,则用或命令。
碰到问题:通信协议接触较少,内容比较抽象,入手难。
处理方法:和队友商议,查阅大量资料,分工协作,从皮毛到深入,经过不停地尝试,一步一步处理问题,虽不能实现功效要求,最关键是学到了知识,以后碰到这么问题将更有信心处理。






