找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 10048|回复: 1
收起左侧

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

[复制链接]
ID:71259 发表于 2014-12-30 00:53 | 显示全部楼层 |阅读模式
  1. /**********************************************************************************
  2. 一般的单片机多机通讯的情况是:
  3. 1.PC向特定单片机发送命令,该单片机收到后回应PC,也就是说不存在几个单片机同时向PC发数据的情况。
  4. 2.PC以广播的形式发送命令,也就是所有单片机都收到命令,在这种情况下一般都是对单片机进行初始设置(此时所有单片机一般都不做应答)
  5. **********************************************************************************/

  6. #include<reg51.h>
  7. #include<string.h>

  8. #define  uchar unsigned char
  9. #define  uint   unsigned int
  10.      
  11. /* 通信命令 */
  12. #define __START_   0x0c    //起始标志位
  13. #define __STOP_    0xc0    //结束标志位
  14. #define __ACTIVE_  0x01    // 主机询问从机是否存在
  15. #define __GETDATA_ 0x02    // 主机发送读设备请求
  16. #define __OK_      0x03    // 从机应答
  17. #define __STATUS_  0x04    // 从机发送设备状态信息

  18. /****************************帧格式****************************
  19.    -目的地址-命令字-数据-校验码-      至少4个字节
  20.   主机地址为0x00, 从机地址为1-244,广播地址255

  21.    转义字符的处理:
  22.    0xdb 0xdd 代表 0xdb
  23.   0xdb 0xdc 代表 0xc0
  24.   0xdb 0xde 代表 0x0c
  25. *************************************************************/

  26. #define MAXSIZE 0x0a    // 缓冲区长度10
  27. uchar send_buf[MAXSIZE]={0xdb,0x0c,0xc0};    // 该缓冲区用于保存设备状态信息
  28. //uchar send_buf[MAXSIZE]={0xcd,0xcb,0xbc};
  29. uchar rec_buf1[MAXSIZE];  // 保存接收到的帧
  30. uchar rec_buf2[MAXSIZE];   //保存处理过的帧

  31. uchar dev;    // 该字节用于保存本机设备号
  32. uchar len ;   // 该字节用于保存发送设备信息的长度
  33. uchar type;   // 该字节用于保存命令字

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

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


  40. uchar Recv_Flag,Recv_Over_Flag;  //接受允许标志,接受完成标志
  41. uchar uart_i;    //帧数据计数

  42. void main()
  43. {
  44. /* 系统初始化 */
  45. dev =0xff;  //设备号

  46. TMOD = 0x20; // 定时器T1使用工作方式2
  47. TH1 = 0xfd;    // 设置初值
  48. TL1 = 0xfd;
  49. TR1 = 1;    // 开始计时
  50. PCON = 0x00;    // bps不倍增
  51. SCON = 0x50; // 工作方式1,波特率9600bps,允许接收
  52. ES = 1;    // 开串口中断
  53. EA = 1;    // 开启中断

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


  56. /* 主程序流程 */
  57. while(1)    // 主循环
  58. {
  59.   if(Recv_Over_Flag==1){
  60.       if(recv_cmd(&type) ==1){
  61.       switch(type)
  62.      {
  63.         case __ACTIVE_:    // 主机询问从机是否存在
  64.             send_data(__OK_, 0, send_buf);    // 发送应答信息,这里buf的内容并未用到
  65.             break;
  66.         case __GETDATA_:
  67.             len=strlen(send_buf);
  68.             send_data(__STATUS_, len, send_buf);    // 发送设备状态信息
  69.                break;
  70.         default:
  71.         break;    // 命令类型错误,丢弃当前帧后返回
  72.             
  73.      }
  74.      Recv_Over_Flag=0;   //一定要清零
  75.      uart_i=0;      //
  76.    }
  77.   }   

  78. }
  79. }


  80. /* 该函数接收一帧数据并进行检测,无论该帧是否错误,函数均会返回
  81. * 函数参数type保存接收到的命令字
  82. * 当接收到数据帧错误或其地址位不为0时(非主机发送帧),函数返回0,反之返回1
  83. */

  84. bit recv_cmd(uchar *type)
  85. {
  86. bit db = 0;    // 当接收到的上一个字节为0xdb时,该位置位
  87. bit c0 = 0;    // 当接收到的上一个字节为0xc0时,该位置位



  88. uchar sum = 0;
  89. uchar i,j;

  90. // ES=0;

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

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

  136. for(i=0;i<j-1;i++)
  137.   sum+=rec_buf2[i];
  138. sum=sum%256;
  139. if(sum !=rec_buf2[j-1] )    // 校验错误,返回
  140.       return 0;


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

  143. *type = rec_buf2[1];    // 获得命令字

  144. return 1;    // 函数成功返回
  145. }


  146. // 该函数发送一帧数据帧,参数type为命令字、len为数据长度、buf为要发送的数据内容
  147. void send_data(uchar type, uchar len, uchar *send_buf)
  148. {

  149. uchar i;
  150. uchar sum = 0;    // 该字节用于保存校验字节
  151.   
  152. ES=0; //关串口中断!!!!

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

  155. TI = 0;    // 发送帧开始标志
  156. SBUF = 0x0c;
  157. while(!TI);
  158. TI=0;

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

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

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

  176. ES=1;  //开串口中断
  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.    
  189.         SBUF = 0xdd;
  190.         while(!TI)
  191.         TI = 0;
  192.    
  193.         break;  
  194.     case 0x0c:    // 字节为0x0c,发送0xdbde
  195.      TI = 0;
  196.         SBUF = 0xdb;
  197.         while(!TI);
  198.         TI = 0;
  199.    
  200.         SBUF = 0xde;
  201.         while(!TI)
  202.         TI = 0;
  203.    
  204.         break;

  205.        case 0xc0:    // 字节为0xc0,发送0xdbdc
  206.         TI = 0;
  207.         SBUF = 0xdb;
  208.         while(!TI);
  209.         TI = 0;
  210.    
  211.         SBUF = 0xdc;
  212.         while(!TI)
  213.         TI = 0;
  214.    
  215.         break;  
  216.          
  217.     default:    // 普通数据则直接发送
  218.         TI = 0;
  219.         SBUF = da;
  220.         while(!TI);
  221.         TI = 0;
  222. }
  223. }



  224. void uart( ) interrupt 4          //串口中断
  225. {
  226. uchar tmp;

  227. if( RI==1 )
  228. {
  229.   RI = 0;
  230.   tmp  = SBUF;  //接收数据
  231.   if(tmp==__START_) //帧起始标志
  232.   {
  233.          uart_i=0;
  234.    Recv_Flag=1;  //接受数据标志
  235.   }
  236.   else if(tmp==__STOP_) //帧结束标志
  237.   {
  238.    if(Recv_Flag==1)
  239.     Recv_Over_Flag=1;
  240.    Recv_Flag=0;
  241.   }
  242.   else if(Recv_Flag==1)  //开始接受帧数据
  243.   {
  244.    rec_buf1[uart_i]=tmp;
  245.    uart_i++;
  246.   }
  247.    if( uart_i >= MAXSIZE )  //超过缓冲区长度
  248.    {
  249.     uart_i = 0;
  250.    Recv_Flag=0;
  251.        Recv_Over_Flag = 1;       //接收一帧数据完毕            
  252.    }
  253. }

  254.   
  255. }
复制代码


回复

使用道具 举报

ID:136238 发表于 2016-9-10 21:21 | 显示全部楼层
请问是主机程序。还是从机,我不懂
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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