×
单片机 > 单片机程序设计 > 详情

单片机实现485总线现场监测系统

发布时间:2020-06-10 发布时间:
|
在一些要求响应速度快、实时性强、控制量多的应用场合,往往理由多个单片机结合PC机组成分布系统,在这样的系统中可以使用RS-485接口连接单片机和PC机。RS-485是RS-232的改良标准,在通信速率、传输距离、多机连接等方面较RS-232有了很大的提高,在软件设计上和RS-232基本一致。在结合RS-485及有关资料基础上学习了用单片机实现485总线现场监测系统。这个系统以PC机为主机,多个单片机为从机的现场监测系统,单片机组组成的各个节点负责采集终端设备的状态信息,主机以轮询的方式向各个节点获取这些设备信息,并根据信息内容进行相关的操作。

 

主要器件:

1、  PC机端的232/485转换接口:MC1488和MC1489实现TTL电平和RS-232通信电平的转化;PC147光电隔离器件;MAX481485驱动收发芯片。

2、  单片机端:AT89C52单片机芯片,用于数据采集和与485总线接口;MAX481485驱动收发芯片;DIP-6开关用于确定本机的设备号。

 

试验流程图:

主机端流程:

 


单片机端流程图:

 


试验电路图:

主机端

 

单片机端:

 

试验程序代码:

// 485Mon.h 程序

#ifndef    _485MON_H                             // 防止485Mon.h被重复引用

 

#define    _485MON_H

 

#include                    // 引用标准库的头文件

#include

#include

 

#define uchar unsigned char

#define uint unsigned int

 

#define ACTIVE  0x11

#define GETDATA       0x22

#define READY           0x33

#define SENDDATA      0x44      

 

#define RECFRMMAXLEN 16            // 接收帧的最大长度,超过此值认为帧超长错误

#define STATUSMAXLEN 10                     // 设备状态信息最大长度    

 

uchar DevNo;                             // 设备号

xdata uchar StatusBuf[STATUSMAXLEN];

 

//为简化起见,假设了10位固定的采集数据

#define DATA0     0x10

#define DATA1     0x20

#define DATA2     0x30

#define DATA3     0x40

#define DATA4     0x50

#define DATA5     0x60

#define DATA6     0x70

#define DATA7     0x80

#define DATA8     0x90

#define DATA9     0xA0

 

sbit DE = P1^6;                    //驱动器使能,1有效

sbit RE = P1^7;                    //接收器使能,0有效

 

void init();                                  // 系统初始化

void Get_Stat();                          // 简化的数据采集函数

bit Recv_Data(uchar *type);         // 接收数据帧函数

void Send(uchar m);                                  // 发送单字节数据

void Send_Data(uchar type,uchar len,uchar *buf);                           // 发送数据帧函数      

void Clr_StatusBuf();                   //  清除设备状态信息缓冲区函数  

 

#endif

 

// 485Mon.c程序

#include "485Mon.h"

 

void main(void)

{

       uchar type;

 

       /* 初始化 */

       init();

      

       while (1)

       {

              if (Recv_Data(&type)==0)                  // 接收帧错误或者地址不符合,丢弃

                     continue;

              switch (type)

              {

                     case ACTIVE:                      // 主机询问从机是否在位

                            Send_Data(READY,0,StatusBuf);  // 发送READY指令

                            break;

                     case GETDATA:                          // 主机读设备请求

                            Clr_StatusBuf();

                            Get_Stat();                          // 数据采集函数

                            Send_Data(SENDDATA,strlen(StatusBuf),StatusBuf);

                            break;

                     default:

                            break;                                 // 指令类型错误,丢弃当前帧

              }

       }

}

 

/* 初始化 */

void init(void)

{

       P1 = 0xff;

       DevNo = (P1&0x00111111);                // 读取本机设备号

 

       TMOD = 0x20;

       SCON = 0x50;

       TH1 = 0xfd;

       TL1 = 0xfd;

       TR1 = 1;

       PCON = 0x00;                                          // SMOD=0

       EA = 0;

                                         

}

 

/* 接收数据帧函数,实际上接收的是主机的指令 */

bit Recv_Data(uchar *type)

{

       uchar tmp,rCount,i;

       uchar r_buf[RECFRMMAXLEN];               // 保存接收到的帧

       uchar Flag_RecvOver;                        // 一帧接收结束标志  

       uchar Flag_StartRec;                          // 一帧开始接收标志

       uchar CheckSum;                                      // 校验和

       uchar DataLen;                                         // 数据字节长度变量

             

       /* 禁止发送,允许接收 */

       DE = 0;

       RE = 0;

 

       /* 接收一帧数据 */

       rCount = 0;

       Flag_StartRec = 0;

    Flag_RecvOver = 0;

       while (!Flag_RecvOver)

       {

              RI = 0;

              while (!RI);

              tmp = SBUF;

              RI=0;

 

              /* 判断是否收到字符'$',其数值为0x24 */         

              if ((!Flag_StartRec) && (tmp == 0x24))

              {

                     Flag_StartRec = 1;

              }

 

              if (Flag_StartRec)

              {

                     r_buf[rCount] = tmp;

                     rCount ++;           

                    

                     /* 判断是否收到字符'*',其数值为0x2A,根据接收的指令设置相应标志位 */

                     if (tmp == 0x2A)

                            Flag_RecvOver = 1;

              }

 

              if (rCount == RECFRMMAXLEN)              // 帧超长错误,返回0

                     return 0;

       }
 

接上篇程序:

 

/* 计算校验和字节 */

       CheckSum = 0;

       DataLen = r_buf[3];

       for (i=0;i++;i<3+DataLen)

       {

              CheckSum = CheckSum + r_buf[i+1];

       }

      

       /* 判断帧是否错误 */

       if (rCount<6)                                     // 帧过短错误,返回0,最短的指令帧为6个字节                                   

              return 0;

       if (r_buf[1]!=DevNo)                        // 地址不符合,错误,返回0

              return 0;

       if (r_buf[rCount-2]!=CheckSum)         // 校验错误,返回0

           return 0;

 

       *type = r_buf[2];                               // 获取指令类型

 

       return 1;                                            // 成功,返回1

}

 

/* 发送数据帧函数 */

void Send_Data(uchar type,uchar len,uchar *buf)

{

       uchar i,tmp;

       uchar CheckSum = 0;

      

       /* 允许发送,禁止接收 */

       DE = 1;

       RE = 1;

      

       /* 发送帧起始字节 */

       tmp = 0x24;

       Send(tmp);

      

       Send(DevNo);                                    // 发送地址字节,也即设备号

       CheckSum = CheckSum + DevNo;

 

       Send(type);                                               // 发送类型字节

       CheckSum = CheckSum + type;

 

       Send(len);                                                 // 发送数据长度字节

       CheckSum = CheckSum + len;

 

       /* 发送数据 */

       for (i=0;i

       {

              Send(*buf);

              CheckSum = CheckSum + *buf;

              buf++;

       }

      

       Send(CheckSum);                                     // 发送校验和字节

 

       /* 发送帧结束字节 */

       tmp = 0x2A;

       Send(tmp);

}

 

/* 采集数据函数经过简化处理,取固定的10个字节数据 */

void Get_Stat(void)

{

       StatusBuf[0]=DATA0;

       StatusBuf[1]=DATA1;

       StatusBuf[2]=DATA2;

       StatusBuf[3]=DATA3;

       StatusBuf[4]=DATA4;

       StatusBuf[5]=DATA5;

       StatusBuf[6]=DATA6;

       StatusBuf[7]=DATA7;

       StatusBuf[8]=DATA8;

       StatusBuf[9]=DATA9;

}

 

/* 发送单字节数据 */

void Send(uchar m)

{

       TI = 0;

       SBUF = m;

    while(!TI);

       TI = 0;   

}

 

/* 清除设备状态信息缓冲区函数*/

void Clr_StatusBuf(void)

{

       uchar i;

       for (i=0;i

              StatusBuf[i] = 0;   

}   

关键字:单片机  485总线  现场监测系统 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
8051单片机的函数发生器的设计