⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 text1.c

📁 485通信程序 对主机从机通信协议规定其通信方式 对帧的传送有严格定义
💻 C
字号:
#ifndef __485_C__
#define __485_C__

#include <reg51.h>
#include <string.h>
 
#define unsigned char uchar
#define unsigned int uint

/* 通信命令 */
#define __ACTIVE_ 0x01    // 主机询问从机是否存在
#define __GETDATA_ 0x02    // 主机发送读设备请求
#define __OK_ 0x03    // 从机应答
#define __STATUS_ 0x04    // 从机发送设备状态信息

#define __MAXSIZE 0x08    // 缓冲区长度
#define __ERRLEN 12    // 任何通信帧长度超过12则表示出错
uchar dbuf[__MAXSIZE];    // 该缓冲区用于保存设备状态信息
uchar dev;    // 该字节用于保存本机设备号

sbit M_DE = P1^0;    // 驱动器使能,1有效
sbit M_RE = P1^1;    // 接收器使能,0有效

void get_status();    // 调用该函数获得设备状态信息,函数代码未给出
void send_data(uchar type, uchar len, uchar *buf);    // 发送数据帧
bit recv_cmd(uchar *type);    // 接收主机命令,主机请求仅包含命令信息
void send_byte(uchar da);    // 该函数发送一帧数据中的一个字节,由send_data()函数调用

void main()
{
 uchar type;
 uchar len;
 
 /* 系统初始化 */
 P1 = 0xff;    // 读取本机设备号
 dev = (P1>>2);
 TMOD = 0x20; // 定时器T1使用工作方式2
 TH1 = 250;    // 设置初值
 TL1 = 250;
 TR1 = 1;    // 开始计时
 PCON = 0x80;    // SMOD = 1
 SCON = 0x50; // 工作方式1,波特率9600bps,允许接收
 ES = 0;    // 关闭串口中断
 IT0 = 0;    // 外部中断0使用电平触发模式
 EX0 = 1;    // 开启外部中断0
 EA = 1;    // 开启中断

 /* 主程序流程 */
 while(1)    // 主循环
 {
  if(recv_cmd(&type) == 0)    // 发生帧错误或帧地址与本机地址不符,丢弃当前帧后返回
   continue;
  switch(type)
  {
   case __ACTIVE_:    // 主机询问从机是否存在
    send_data(__OK_, 0, dbuf);    // 发送应答信息,这里buf的内容并未用到
    break;
   case __GETDATA_:
    len = strlen(dbuf);
    send_data(__STATUS_, len, dbuf);    // 发送设备状态信息
    break;
   default:
    break;    // 命令类型错误,丢弃当前帧后返回
  }
 }
}

void READSTATUS() interrupt 0 using 1    // 产生外部中断0时表示设备状态发生改变,该函数使用寄存器组1
{
 get_status();    // 获得设备状态信息,并将其存入dbuf指向的存储区,数据最后一字节置0表示数据结束
}

/* 该函数接收一帧数据并进行检测,无论该帧是否错误,函数均会返回
 * 函数参数type保存接收到的命令字
 * 当接收到数据帧错误或其地址位不为0时(非主机发送帧),函数返回0,反之返回1
 */
bit recv_cmd(uchar *type)
{
 bit db = 0;    // 当接收到的上一个字节为0xdb时,该位置位
 bit c0 = 0;    // 当接收到的上一个字节为0xc0时,该位置位
 uchar data_buf[__ERRLEN];    // 保存接收到的帧
 uchar tmp;
 uchar ecc = 0;
 uchar i;
 
 M_DE = 0;    // 置发送禁止,接收允许
 M_RE = 0;
 
 /* 接收一帧数据 */
 i = 0;
 while(!c0)    // 循环直至帧接收完毕
 {
  RI = 0;
  while(!RI);
  tmp = SBUF;
  RI = 0;
  if(db == 1)    // 接收到的上一个字节为0xdb
  {
   switch(tmp)
   {
    case 0xdd:
     data_buf[i] = 0xdb;    // 0xdbdd表示0xdb
     ecc = ecc^0xdb;
     db = 0;
     break;
    case 0xdc
     data_buf[i] = 0xc0;    // 0xdbdc表示0xc0
     ecc = ecc^0xc0;
     db = 0;
     break;
    default
     return 0;    // 帧错误,返回
   }
   i++;
  }
  switch(tmp)    // 正常情况
  {
   case 0xc0:    // 帧结束
    c0 = 1;
    break;
   case 0xdb:    // 检测到转义字符
    db = 1;
    break;
   default:    // 普通数据
    data_buf[i] = tmp;    // 保存数据
    ecc = ecc^tmp;    // 计算校验字节
    i++;
  }
  if(i == __ERRLEN)    // 帧超长,错误,返回
   return 0;
 }
 /* 判断帧是否错误 */
 if(i<4)    // 帧过短,错误,返回
  return 0;
 if(ecc != 0)    // 校验错误,返回
  return 0;
 if(data_buf[0] != dev)    // 非访问本机命令,错误,返回
  return 0;
 *type = data_buf[1];    // 获得命令字
 return 1;    // 函数成功返回
}

/* 该函数发送一帧数据帧,参数type为命令字、len为数据长度、buf为要发送的数据内容 */
void send_data(uchar type, uchar len, uchar *buf)
{
 uchar i;
 uchar ecc = 0;    // 该字节用于保存校验字节

 M_DE = 1;    // 置发送允许,接收禁止
 M_RE = 1;
 
 send_byte(dev);    // 发送本机地址
 ecc = dev;
 send_byte(type);    // 发送命令字
 ecc = ecc^type;
 send_byte(len);    // 发送长度
 ecc = ecc^len;
 for(i=0; i<len; i++)    // 发送数据
 {
  send_byte(*buf);
  ecc = ecc^(*buf);
  buf++;
 }
 send_byte(ecc);    // 发送校验字节
 
 TI = 0;    // 发送帧结束标志
 SBUF = 0xc0;
 while(!TI);
 TI = 0;
}

/* 该函数发送一个数据字节,若该字节为0xdb,则发送0xdbdd,若该字节为0xc0则,发送0xdbdc */
void send_byte(uchar da)
{
 switch(da)
 {
  case 0xdb:    // 字节为0xdb,发送0xdbdd
   TI = 0;
   SBUF = 0xdb;
   while(!TI);
   TI = 0;
   SBUF = 0xdd;
   while(!TI)
   TI = 0;
   break;
  case 0xc0:    // 字节为0xc0,发送0xdbdc
   TI = 0;
   SBUF = 0xdb;
   while(!TI);
   TI = 0;
   SBUF = 0xdc;
   while(!TI)
   TI = 0;
   break;
  default:    // 普通数据则直接发送
   TI = 0;
   SBUF = da;
   while(!TI);
   TI = 0;
 }
}

#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -