找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3492|回复: 0
收起左侧

51单片机实现的485通讯程序①

[复制链接]
ID:71259 发表于 2014-12-30 00:52 | 显示全部楼层 |阅读模式
  1. #include<reg51.h>
  2. #include<string.h>

  3. #define  uchar unsigned char
  4. #define  uint   unsigned int
  5.      
  6. /* 通信命令 */
  7. #define __ACTIVE_  0x01    // 主机询问从机是否存在
  8. #define __GETDATA_ 0x02    // 主机发送读设备请求
  9. #define __OK_      0x03    // 从机应答
  10. #define __STATUS_  0x04    // 从机发送设备状态信息

  11. /****************************帧格式****************************
  12.   目的地址-本机地址-命令字-数据-校验码-结束标志     至少5个字节
  13. *************************************************************/

  14. #define MAXSIZE 0x08    // 缓冲区长度
  15. #define ERRLEN 12       // 任何通信帧长度超过12则表示出错
  16. uchar send_buf[MAXSIZE]={0xc0,0xdb};    // 该缓冲区用于保存设备状态信息
  17. uchar rec_buf[ERRLEN];    // 保存接收到的帧

  18. uchar dev;    // 该字节用于保存本机设备号

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

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




  25. void main()
  26. {
  27. uchar type;
  28. uchar len=2;

  29. /* 系统初始化 */

  30. dev =0xff;  //设备号

  31. TMOD = 0x20; // 定时器T1使用工作方式2
  32. TH1 = 0xfd;    // 设置初值
  33. TL1 = 0xfd;
  34. TR1 = 1;    // 开始计时
  35. PCON = 0x00;    // bps不倍增
  36. SCON = 0x50; // 工作方式1,波特率9600bps,允许接收
  37. ES = 1;    // 开串口中断
  38. IT0 = 0;    // 外部中断0使用电平触发模式
  39. EX0 = 1;    // 开启外部中断0
  40. EA = 1;    // 开启中断

  41. /* 主程序流程 */
  42. while(1)    // 主循环
  43. {

  44.      if(recv_cmd(&type) == 0)    // 发生帧错误或帧地址与本机地址不符,丢弃当前帧后返回
  45.        continue;

  46.      SBUF = rec_buf[0];
  47.       while(!TI);
  48.        TI = 0;
  49.          SBUF = rec_buf[1];
  50.       while(!TI);
  51.        TI = 0;
  52.          SBUF = rec_buf[2];
  53.       while(!TI);
  54.        TI = 0;
  55.          SBUF = rec_buf[3];
  56.       while(!TI);
  57.        TI = 0;
  58.          SBUF = rec_buf[4];
  59.       while(!TI);
  60.        TI = 0;
  61.   /*
  62.     switch(type)
  63.     {
  64.      case __ACTIVE_:    // 主机询问从机是否存在
  65.       send_data(__OK_, 0, send_buf);    // 发送应答信息,这里buf的内容并未用到
  66.       break;
  67.      case __GETDATA_:
  68.       len = strlen(send_buf);
  69.       send_data(__STATUS_, len, send_buf);    // 发送设备状态信息
  70.       break;
  71.      default:
  72.       break;    // 命令类型错误,丢弃当前帧后返回
  73.     }  
  74.       */
  75. }
  76. }


  77. /* 该函数接收一帧数据并进行检测,无论该帧是否错误,函数均会返回
  78. * 函数参数type保存接收到的命令字
  79. * 当接收到数据帧错误或其地址位不为0时(非主机发送帧),函数返回0,反之返回1
  80. */
  81. bit recv_cmd(uchar *type)
  82. {
  83. bit db = 0;    // 当接收到的上一个字节为0xdb时,该位置位
  84. bit c0 = 0;    // 当接收到的上一个字节为0xc0时,该位置位

  85. uchar tmp;
  86. uchar sum = 0;
  87. uchar i,j;

  88. M_DE = 0;    // 置发送禁止,接收允许
  89. M_RE = 0;

  90. /* 接收一帧数据 */
  91. i = 0;
  92. while(!c0)    // 循环直至帧接收完毕
  93. {
  94.     RI = 0;
  95.     while(!RI);
  96.     tmp = SBUF;
  97.     RI = 0;
  98.     if(db == 1)    // 接收到的上一个字节为0xdb
  99.     {
  100.         switch(tmp)
  101.         {
  102.         case 0xdd:
  103.          rec_buf[i] = 0xdb;    // 0xdbdd表示0xdb
  104.       
  105.          db = 0;
  106.          break;
  107.         case 0xdc:
  108.          rec_buf[i] = 0xc0;    // 0xdbdc表示0xc0
  109.      
  110.          db = 0;
  111.          break;
  112.         default :
  113.          return 0;    // 帧错误,返回
  114.         }
  115.       i++;
  116.     }
  117.    else{
  118.       switch(tmp)    // 正常情况
  119.       {
  120.        case 0xc0:    // 帧结束
  121.         c0 = 1;
  122.   
  123.         break;
  124.        case 0xdb:    // 检测到转义字符
  125.         db = 1;
  126.    
  127.         break;
  128.        default:    // 普通数据
  129.         rec_buf[i] = tmp;    // 保存数据
  130.         // 计算校验字节
  131.         i++;
  132.       }
  133.      if(i == ERRLEN)    // 帧超长,错误,返回
  134.       return 0;
  135.    }
  136.   }
  137.   
  138. /* 判断帧是否错误 */
  139. if(i<4)    // 帧过短,错误,返回
  140.     return 0;

  141. for(j=0;j<i-1;j++)
  142.   sum+=rec_buf[j];
  143. sum=sum%256;
  144. if(sum !=rec_buf[i-1] )    // 校验错误,返回
  145.     return 0;

  146. if(rec_buf[0] != dev)    // 非访问本机命令,错误,返回
  147.     return 0;

  148. *type = rec_buf[2];    // 获得命令字

  149. return 1;    // 函数成功返回
  150. }


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

  156. M_DE = 1;    // 置发送允许,接收禁止
  157. M_RE = 1;

  158. send_byte(0x00);    // 发送目的地址
  159. sum+=0x00;  //假设主机地址为0x00
  160. send_byte(dev);    // 发送本机地址
  161. sum+=dev;
  162. send_byte(type);    // 发送命令字
  163. sum+=type;
  164. //send_byte(len);    // 发送长度

  165. for(i=0; i<len; i++)    // 发送数据
  166. {
  167.     send_byte(*send_buf);
  168.     sum =sum+ (*send_buf);
  169.     send_buf++;
  170. }
  171. sum%=256;
  172. send_byte(sum);    // 发送校验字节

  173. TI = 0;    // 发送帧结束标志
  174. SBUF = 0xc0;
  175. while(!TI);
  176. TI = 0;
  177. }

  178. //该函数发送一个数据字节,若该字节为0xdb,则发送0xdbdd,若该字节为0xc0则,发送0xdbdc
  179. void send_byte(uchar da)
  180. {
  181. switch(da)
  182. {
  183.     case 0xdb:    // 字节为0xdb,发送0xdbdd
  184.      TI = 0;
  185.      SBUF = 0xdb;
  186.      while(!TI);
  187.      TI = 0;
  188.      SBUF = 0xdd;
  189.      while(!TI)
  190.      TI = 0;
  191.      break;
  192.     case 0xc0:    // 字节为0xc0,发送0xdbdc
  193.      TI = 0;
  194.      SBUF = 0xdb;
  195.      while(!TI);
  196.      TI = 0;
  197.      SBUF = 0xdc;
  198.      while(!TI)
  199.      TI = 0;
  200.      break;
  201.     default:    // 普通数据则直接发送
  202.      TI = 0;
  203.      SBUF = da;
  204.      while(!TI);
  205.      TI = 0;
  206. }
  207. }
复制代码


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表