查看: 782|回复: 0

[基础] 51单片机多机通信

[复制链接]
  • TA的每日心情

    2018-8-2 13:58
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]初来乍到

    发表于 2019-10-28 10:04:09 | 显示全部楼层 |阅读模式
    分享到:
    一、多机通信原理
        在多机通信中,主机必须要能对各个从机进行识别,在51系列单片机中可以通过SCON寄存器的SM2位来实现。当串口以方式2或方式3发送数据时,每一帧信息都是11位,第9位是数据可编程位,通过给TB8置1或置0来区别地址帧和数据帧,当该位为1时,发送地址帧;该位为0时,发送数据帧。

        在多机通信过程中,主机先发送某一从机的地址,等待从机的应答,所有的从机接收到地址帧后与本机地址进行比较,若相同,则将SM2置0准备接收数据;若不同,则丢弃当前数据,SM2位不变。

    二、多机通信电路图

        此处,U1作为主机,U2为从机1,U3为从机2。

    三、C语言程序
    (1)主机程序

    #include<reg51.h>
    #include<string.h>
    #define _SUCC_   0x0f//数据传送成功
    #define _ERR_    0xf0//数据传送失败
    unsigned char Table[9]={0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};
    unsigned char Buff[20];  //数据缓冲区
    unsigned char temp=0xff;
    sbit KEY1=P1^6;
    sbit KEY2=P1^7;
    //unsigned char addr;
    //延时1ms函数
    void delay_1ms(unsigned int t)
    {
    unsigned int x,y;
    for(x=t;x>0;x--)
      for(y=110;y>0;y--);
    }
    //缓冲区初始化
    void Buff_init()
    {
    unsigned char i;    //将Table里的数据放到缓冲区里
    for(i=0;i<9;i++)  
    {
      Buff= Table;
      delay_1ms(100);
    }
    }
    //串口初始化函数
    void serial_init()
    {
    TMOD=0x20; //定时器1工作于方式2
    TH1=0xfd;  
    TL1=0xfd; //波特率为9600
    PCON=0;
    SCON=0xd0;  //串口工作于方式3
    TR1=1;  //开启定时器
    TI=0;
    RI=0;
    }
    //发送数据函数
    void SEND_data(unsigned char *Buff)
    {
    unsigned char i;
    unsigned char lenth;
    unsigned char check;
    lenth=strlen(Buff);      //计算数据长度
    check=lenth;
    TI=0;         //发送数据长度
    TB8=0;      //发送数据帧
    SBUF=lenth;
    while(!TI);
    TI=0;
             
    for(i=0;i<lenth;i++)  //发送数据
    {
      check=check^Buff;
      TB8=0;
      SBUF=Buff;   
      while(!TI);
      TI=0;
    }
    TB8=0;      //发送校验字节
    SBUF=check;   
    while(!TI);
    TI=0;     
    }
    //向指定从机地址发送数据
    void ADDR_data(unsigned addr)
    {
    while(temp!=addr) //主机等待从机返回其地址作为应答信号
    {
      TI=0;    //发送从机地址
      TB8=1;    //发送地址帧
      SBUF=addr;
      while(!TI);
      TI=0;
      
      RI=0;
      while(!RI);
      temp=SBUF;
      RI=0;
    }
    temp=_ERR_;   //主机等待从机数据接收成功信号
    while(temp!=_SUCC_)
    {
      SEND_data(Buff);
      RI=0;
      while(!RI);
      temp=SBUF;
      RI=0;
    }
    }      

    void main()
    {
    Buff_init();
    serial_init();
    while(1)
    {
      if(KEY1==0)
      {
       delay_1ms(5);
       if(KEY1==0)
       {
        while(!KEY1);
        ADDR_data(0x01);
       }
      }
      if(KEY2==0)
      {
       delay_1ms(5);
       if(KEY2==0)
       {
        while(!KEY2);
        ADDR_data(0x02);
       }
      }
    }
    }

    (2)从机1程序
    #include<reg51.h>
    #include<string.h>
    #define addr     0x01//从机1的地址
    #define _SUCC_   0x0f//数据传送成功
    #define _ERR_    0xf0//数据传送失败
    unsigned char aa=0xff;//主机与从机之间通信标志
    unsigned char Buff[20];//数据缓冲区
    //串口初始化函数
    void serial_init()
    {
    TMOD=0x20; //定时器1工作于方式2
    TH1=0xfd;  
    TL1=0xfd; //波特率为9600
    PCON=0;
    SCON=0xd0;  //串口工作于方式3
    TR1=1;  //开启定时器
    TI=0;
    RI=0;
    }
    //接收数据函数
    unsigned char RECE_data(unsigned char *Buff)
    {
    unsigned char i,temp;
    unsigned char lenth;
    unsigned char check;
    RI=0;     //接收数据长度
    while(!RI);
    if(RB8==1)    //若接收到地址帧,则返回0xfe
      return 0xfe;
    lenth=SBUF;
    RI=0;     

    check=lenth;
    for(i=0;i<lenth;i++) //接收数据
    {
      while(!RI);
      if(RB8==1)   //若接收到地址帧,则返回0xfe
       return 0xfe;
      Buff=SBUF;   
      check=check^(Buff);
      RI=0;
    }
    while(!RI);    //接收校验字节
    if(RB8==1)    //若接收到地址帧,则返回0xfe
      return 0xfe;
    temp=SBUF;
    RI=0;
          
    check=temp^check;  //将从主机接收到的校验码与自己计算的校验码比对
    if(check!=0)   //校验码不一致,表明数据接收错误,向主机发送错误信号,函数返回0xff
    {
      TI=0;
      TB8=0;
      SBUF=_ERR_;
      while(!TI);
      TI=0;
      return 0xff;
    }
    TI=0;           //校验码一致,表明数据接收正确,向主机发送成功信号,函数返回0x00
    TB8=0;
    SBUF=_SUCC_;
    while(!TI);
    TI=0;
    return 0;
    }

    void main()
    {
    serial_init();
    while(1)
    {
      SM2=1;              //接收地址帧
      while(aa!=addr)  //从机等待主机请求自己的地址
      {
       RI=0;
       while(!RI);
       aa=SBUF;
       RI=0;
      }
      TI=0;     //一旦被请求,从机返回自己的地址作为应答,等待接收数据
      TB8=0;
      SBUF=addr;
      while(!TI);
      TI=0;
      SM2=0;                  //接收数据帧
      aa=0xff;    //从机接收数据,并将数据保存到数据缓冲区
      while(aa==0xff)
      {
       aa=RECE_data(Buff);
      }
      if(aa==0xfe)
       continue;
      P1=Buff[1];      //查看接收到的数据
    }
    }

    (3)从机2程序

    #include<reg51.h>
    #include<string.h>
    #define addr     0x02//从机2的地址
    #define _SUCC_   0x0f//数据传送成功
    #define _ERR_    0xf0//数据传送失败
    unsigned char aa=0xff;//主机与从机之间通信标志
    unsigned char Buff[20];//数据缓冲区
    //串口初始化函数
    void serial_init()
    {
    TMOD=0x20; //定时器1工作于方式2
    TH1=0xfd;  
    TL1=0xfd; //波特率为9600
    PCON=0;
    SCON=0xd0;  //串口工作于方式3
    TR1=1;  //开启定时器
    TI=0;
    RI=0;
    }
    //接收数据函数
    unsigned char RECE_data(unsigned char *Buff)
    {
    unsigned char i,temp;
    unsigned char lenth;
    unsigned char check;
    RI=0;     //接收数据长度
    while(!RI);
    if(RB8==1)    //若接收到地址帧,则返回0xfe
      return 0xfe;
    lenth=SBUF;
    RI=0;     

    check=lenth;
    for(i=0;i<lenth;i++) //接收数据
    {
      while(!RI);
      if(RB8==1)   //若接收到地址帧,则返回0xfe
       return 0xfe;
      Buff=SBUF;   
      check=check^(Buff);
      RI=0;
    }
    while(!RI);    //接收校验字节
    if(RB8==1)    //若接收到地址帧,则返回0xfe
      return 0xfe;
    temp=SBUF;
    RI=0;
          
    check=temp^check;  //将从主机接收到的校验码与自己计算的校验码比对
    if(check!=0)   //校验码不一致,表明数据接收错误,向主机发送错误信号,函数返回0xff
    {
      TI=0;
      TB8=0;
      SBUF=_ERR_;
      while(!TI);
      TI=0;
      return 0xff;
    }
    TI=0;           //校验码一致,表明数据接收正确,向主机发送成功信号,函数返回0x00
    TB8=0;
    SBUF=_SUCC_;
    while(!TI);
    TI=0;
    return 0;
    }

    void main()
    {
    serial_init();
    while(1)
    {
      SM2=1;              //接收地址帧
      while(aa!=addr)  //从机等待主机请求自己的地址
      {
       RI=0;
       while(!RI);
       aa=SBUF;
       RI=0;
      }
      TI=0;     //一旦被请求,从机返回自己地址作为应答,等待接收数据
      TB8=0;
      SBUF=addr;
      while(!TI);
      TI=0;
      SM2=0;                  //接收数据帧
      aa=0xff;    //从机接收数据,并将数据保存到数据缓冲区
      while(aa==0xff)
      {
       aa=RECE_data(Buff);
      }
      if(aa==0xfe)
       continue;
      P1=Buff[2];      //查看接收到的数据
    }
    }

    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-25 03:11 , Processed in 0.119510 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.