资源描述
测控网络课程设计
姓名:
班级:
学号:
同组者:
第一部分 试验要求
1.要求
此次实践以开发计算机测控系统为最终目标,要求掌握计算机测控系统工作原理,学习组态王工控组态软件使用方法,依据要求完成工程组态;掌握MODBUS通信协议原理,开发含有MODBUS通讯功效智能仪表,最终完成和组态工程之间通讯。
2.设计内容和时间计划
(1) 熟悉组态王软件安装,基础开发环境,采取构建简单工程(采取仿真数据和设备,工程应包含PID功效),计划时间1天;
(2) 依据罐区工艺要求,完成对应组态工程,实现对原油储罐监控,计划时间2天;
(3) 掌握MODBUS通讯协议工作原理,在MSP430F5438单片机上编程实现MODBUS用户端服务程序,要求经过串行口将现场温度、泵状态、流量等参数上传到上位机上,计划时间3天。
(4) 在单片机上编程实现流量、温度上下限及仪表地址和波特率等参数设置功效,同时能从上位机对仪表参数进行设置,计划时间2天。
(5) 优化设计,要求当出现通讯错误时在上位机和单片机上全部要做出对应反应,计划时间0.5天。
(6) 上位机采取高级语言编程,实现对现场智能仪表控制。
第6项为有能力者完成,计划时间1.5天。
3.具体设计要求
3.1 组态王工程部分:见附录1
3.2 智能仪表部分:
(1)仪表支持MODBUS ASCII和MODBUS RTU通讯协议。
(2)仪表含有现场参数修改功效(变送器参数、泵参数和通讯参数能够自由设置,且单位能够更改),参数最好掉电不丢失。
(3)仪表含有参数上传功效,能够经过上位机实现对仪表参数设置。
(4)当上位机发送命令有问题时,仪表应进行错误处理;当仪表返回数据有错误时,上位机也应作出反应。
3.3 MODBUS上位机软件:能够正确读写仪表数据。
第二部分 试验内容
组态王部分
1、罐区工艺步骤图
2- 储油罐进口电动阀;3- 储油罐排污电动阀;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)
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;
进口阀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
{
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 && 液位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=液位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;}
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 协议是应用于电子控制器上一个通用语言。经过此协议,控制器相互之间、控制器经由网络(比如以太网)和其它设备之间能够通信。此协议定义了一个控制器能认识使用消息结构,而不管它们是经过何种网络进行通信。它描述了控制器请求访问其它设备过程,假如回应来自其它设备请求,和怎样侦测错误并统计。它制订了消息域格局和内容公共格式。
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 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 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 = 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**
}
// 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)
{
while (!(UCA2IFG & UCTXIFG)); // 判定是否发送完成
if(UCA2RXBUF == ':') //:为起始标志,假如开始,标志位flag置位
{
flag = 1;
tempnumb--;
}
if(flag==1) //当标志位flag置位说明发送命令开始,开始接收命令数据
{
RX[0] = ':';
if(UCA2RXBUF != 0x0D && UCA2RXBUF != 0x0A ) //只要不是回车换行符,就依次将数据存入接收数组RX中,同时计数变量tempnumb加1
{
tempnumb++;
RX[tempnumb] = UCA2RXBUF ;
}
if(UCA2RXBUF == 0x0D) //若是回车
{
tempnumb++;
RX[tempnumb] = 'D' ;
}
if(UCA2RXBUF == 0x0A) //若是换行
{
tempnumb++;
RX[tempnumb] = 'A' ;
flag=0; //接收标志位清零
flag1=1; //发送标志位置1
TTXX(); //调用发送数据函数
}
}
}
4、 单片机经过UART中止向上位机发送应答指令
void TTXX()
{
int i;
int j;
unsigned int b;
unsigned int 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 == 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<temp*4;i+=2)
{
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++) //单片机发送数据
{
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)); // 判定是否发送完成
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]); //设置流量值
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++ ; //缓冲区字节相加,无进位
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效验计算
LRCHi = hextoascii((LRC>>4)&0x0F);
LRCLo = hextoascii(LRC&0x0F);
}
void Out_LRC()
{
unsigned int c;
for(c=1;c<tempnumt;c+=2)
{
tempTX[(c-1)/2] = (asciitohex(TX[c])<<4);
tempTX[(c-1)/2] = (asciitohex(TX[c])<<4) | asciitohex(TX[c+1]);
}
LRCt=MODBUS_LRC(&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 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];
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)
{
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':temperature=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; // 时钟源选择
UCA1BR0 = 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; // 时钟源选择
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)
{
WDTCTL = WDTPW+WDTHOLD; //关闭看门狗
clock_init(); //时钟初始化
lcm_init(); //LCM初始化
RS232_init();
RS232_open();
_EINT(); //打开总中止
while(1)
{ }
}
总结
此次课程实习,关键对组态王学习有了更深认识,能够愈加好使用组态王软件,和了解她工作原理。经过不停学习和使用,也加强了我学习知识,查阅资料能力。此次课程中有很多以前没有碰到过问题,比如画面命令编写,之前是经过PLC,而这次是直接用组态王模拟,需要定义时和命令中变量名称相同。编程思绪和方法和plc相同。经过学习,收益良多,而且这次接触了新内容,通讯协议。这部分内容较难了解,我们也认真努力去学习了解它,即使最终没能将通信部分弄出来,但其中收获是无价,再次感谢老师教导。
碰到问题:组态部分整体设计,管道参数设计,画面命令等部分细节方面问题。
处理方法:经过和同学交流探讨,设计整体画面。查阅资料,和老师交流,设置管道参数等等。比如设置管道参数,需要考虑管道何时流水,和泵关系,拿出进水泵为例,假如三个入水泵全部开才有水需用和命令,若三个进水泵有一个开有水,则用或命令。
碰到问题:通信协议接触较少,内容比较抽象,入手难。
处理方法:和队友商议,查阅大量资料,分工协作,从皮毛到深入,经过不停地尝试,一步一步处理问题,虽不能实现功效要求,最关键是学到了知识,以后碰到这么问题将更有信心处理。
展开阅读全文