打印
[DemoCode下载]

51单片机rs485驱动程序

[复制链接]
1466|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
dentsgot|  楼主 | 2016-5-18 20:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

#include <reg52.h>                            // 引用标准库的头文件
#include <stdio.h>
#include <string.h>

#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();                        //  清除设备状态信息缓冲区函数   
/****************************************/
/* Copyright (c) 2005, 通信工程学院     */
/* All rights reserved.                 */
/****************************************/

#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<len;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<STATUSMAXLEN;i++)
                StatusBuf[ i] = 0;        
}                        


沙发
玛尼玛尼哄| | 2016-5-31 22:10 | 只看该作者
非常不错,最后那个for循环,是提前定义好的长度不。

使用特权

评论回复
板凳
zhuotuzi| | 2016-5-31 22:18 | 只看该作者
原来51单片机也可以做485的通信,那么电路是怎么样的呢,有没有个给看看。

使用特权

评论回复
地板
zhuotuzi| | 2016-5-31 22:19 | 只看该作者
 RS-485串行总线接口标准以差分平衡方式传输信号,具有很强的抗共模干扰的能力,允许一对双绞线上一个发送器驱动多个负载设备。工业现场控制系统中一般都采用该总线标准进行数据传输,而且一般采用RS-485串行总线接口标准的系统都使用8044芯片作为通信控制器或各分机的CPU。8044芯片内部集成了SDLC,HDLC等通信协议,并且集成了相应的硬件电路,通过硬件电路和标准协议的配合,使系统的通讯准确、可靠、快速。8044在市场上日渐稀少,虽然有8344可替代,但几百元的价位与普通单片机几元至几十元的价位相差甚远,用户在开发一般的单片机应用系统时,都希望能用简单的电路和简单的通信协议完成数据交换。譬如:利用单片机本身所提供的简单串行接口,加上总线驱动器如SN75176等组合成简单的RS-485通讯网络。本文所述的方法已成功地应用于工程项目,一台主机与60台从机通讯,通讯波特率达64KBPS。

使用特权

评论回复
5
wahahaheihei| | 2016-5-31 23:14 | 只看该作者
还是当年的51比较好学,简单,好用,东西不多。

使用特权

评论回复
6
稳稳の幸福| | 2016-5-31 23:19 | 只看该作者

        /* 计算校验和字节 */
        CheckSum = 0;
        DataLen = r_buf[3];
        for (i=0;i++;i<3+DataLen)
        {
                CheckSum = CheckSum + r_buf[i+1];
        }

计算完了怎么验证呢

使用特权

评论回复
7
598330983| | 2016-5-31 23:37 | 只看该作者
bit Recv_Data(uchar *type);                // 接收数据帧函数
一帧数据是多少个字节

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

164

帖子

2

粉丝