实现单片机与PC机多机通讯的程序

发布者:Shuangfei最新更新时间:2015-06-25 来源: 51hei关键字:单片机  PC机  多机通讯 手机看文章 扫描二维码
随时随地手机看文章
     下面是我写的一个实现多个下位机(单片机)与一个上位机(PC机)的一主多从串口通讯程序,用的STC89C52RC,定时器2做串口通信波特率发生器。

     实现功能是这样的:
     用调试助手向单片机发送一个数据包。
     通讯协议是这样的:
      数据包的格式如下所示(共10个字节组成):
0x2A,0xEB,0x8D,地址码,指令码,数据长度码,数据码,数据码,校验码,0xAD  
前面三个字节为帧头,即开始符。
地址码: 欲传送的目的地址,即选定哪一个单片机。
指令码:向单片机发送的指令
数据长度码: 用于指示后面有效数据的个数
数据码:传送的数据,配合指令码的纯数据。
校验码: 累加和校验,对地址码,指令码,数据长度码,数据码进行累加,用来检验数据的完整性和正确性。
0xAD : 帧尾,即结束符。

    本程序实现功能是这样的:
    用调试助手向单片机发送一个数据包,单片机收到后对数据解析,再回传指定的数据。
    例如发送:2a eb 8d 01 03 01 01 06 ad
指令码为01,单片机接收到后解析,回传0xce 0x7b 0x11 0xed。其中前两个字节为开始符,最后一个字节为结束符。同理,若收到的指令码为02,回传0xce 0x7b 0x12 0xed。以此模拟控制单片机操作。
若接收错误,即累加校验码不等于单片机实际计算的累加和,回传0xce 0x7b 0x02 0xed,提示接收错误,要求PC重发数据(模拟,需要上位机软件配合才行)。
单片机开机初始化后即向PC发送一个数据0xce 0x7b 0x00 0xed,用于指示单片机与PC通信已连接。

下面是程序:
#define ID 0x01 //单片机地址
uint8 rec_data;   //串口通信接收数据
uint8 state_flag=0;  //通信协议解析状态标志,初始化为0
uint8 retval=0;  //通信协议解析函数返回值,初始化为0
uint8 cmd;  //指令码
uint8 Data[2];  //数据码
uint8 data_count;  //数据长度码

程序大体思想是:
    首先定义了几个全局变量,接收到数据后,串口中断子程序中用变量rec_data存储一个字节的数据,随后对数据进行解析:首先判断数据包的完整性,正确性,然后提取指令码,数据码等数据,存放起来用于主程序处理。
    协议解析过程中,使用一个变量state_flag的全局变量作为协议解析状态标志,用于确定当前字节处于一帧数据中的那个部位,同时在接收过程中自动对接收数据进行校验和处理,在数据包接收完的同时也进行了校验的比较。因此当帧尾结束符接收到的时候,则表示一帧数据已经接收完毕,并且也通过了校验,关键数据也保存到了缓冲区(cmd和Data[])中。主程序即可通过查询retval的标志位来进行协议的解析处理。如果retval=1;   //错误标志,数据包传送不正确。如果retval=2;   //接收成功标志,数据包传送成功。
    接收过程中,只要哪一步收到的数据不是预期值,则直接将状态标志复位,用于下一帧数据的判断,避免状态自锁。
    以下是程序:
void PortInit();                //各端口初始化
void TimerInit();        //定时器初始化
void UsartInit();        //串口初始化
void usart_cmd_scan();        //串口命令扫描
void Data_analysis();   //通信协议解析函数
void Send(uint8 sendcmd);  //数据发送函数


/*--------------------------------        串口中断服务子程序 ------------------------------------*/
void ser() interrupt 4
{
   RI=0;
   rec_data=SBUF;   //读取接收到的数据
   Data_analysis();//数据解析  
}

/*
* 函数名:Data_analysis
* 描  述:通信协议解析函数
* 输  入:无
* 输  出:无
* 备  注:解析串口接收到的数据[page]
/*--------------------------------        多机通信协议格式 ------------------------------------*/
/*  数据包的格式如下所示(共10个字节组成): */
/*  0x2A,0xEB,0x8D,地址码,指令码,数据长度码,数据码,数据码,校验码,0xAD  */
void Data_analysis()
{
   static uchar recdata_sum=0;  //存放累加和
   static uchar lencnt=0;  //数据长度计数器
   switch (state_flag)
     {
        case 0:
          {
             if(rec_data == 0x2A)     // 是否帧头第一个数据
               state_flag = 1;
             else
               state_flag = 0;    // 标志复位 
             break;      
          }
        case 1:
          {
             if(rec_data == 0xEB)     // 是否帧头第二个数据
               state_flag = 2;
             else
               state_flag = 0;    // 标志复位
             break;
          }
        case 2:
          {
             if(rec_data == 0x8D)     // 是否帧头第三个数据
               state_flag = 3;
             else
               state_flag = 0;    // 标志复位
             break;
          }
        case 3:
          {
             if(rec_data == ID)    // 判断目的地址是否正确
               {
                  state_flag = 4;
                  recdata_sum=rec_data;   //开始累加
               }   
             else
               state_flag = 0;   // 标志复位
             break;
          } 
        case 4: 
          {
             state_flag = 5;
             cmd=rec_data;  //指令码存储
             recdata_sum+=rec_data;  //累加
             break;
          }        
        case 5: 
          {
             lencnt = 0;  //数据长度计数器清零
             data_count=rec_data;  //数据长度码存储
             recdata_sum+=rec_data;  //累加
             if (data_count!=0)  //后面有数据码
               state_flag=6;
             else 
               state_flag=8;
             break;
          } 
        case 6: 
        case 7: 
          {
              Data[lencnt++]=rec_data;  //数据码保存
              recdata_sum+=rec_data;   //累加
              if(lencnt==data_count)
              {
                                          state_flag=8;
                                        lencnt = 0;        
                          }  
                                
              else
                state_flag=7;
              break;
          }
        case 8:
          {
             if(recdata_sum==rec_data)   //数据校验,判断累加和是否相等
               state_flag=9;
             else
               {
                  retval=1;   //置错误标志,数据包传送不正确。
                  state_flag=0;   
               }
                         recdata_sum=0;//累加和清零
             break;
          }
        case 9:
          {
             if (rec_data==0xAD)
               {
                                           retval=2;   //置接收成功标志,数据包传送成功。
                                        state_flag=0;
                           }
             else
               state_flag=0; 
             break; 
          }

     }
}

//主程序 , 不断扫描串口接收到的命令
void main()
{
        PortInit();                //各端口初始化
        TimerInit();        //定时器初始化
        UsartInit();        //串口初始化                                   
        Send(0xce);
        Send(0x7b);
        Send(0x00);
        Send(0xed);
        while(1)
        { 
                usart_cmd_scan();        //串口命令扫描
        }        
}


/*
* 函数名:usart_cmd_scan
* 描  述:串口命令扫描
* 输  入:无
* 输  出:无
* 备  注:扫描PC通过串口发送的命令
*/
void usart_cmd_scan()
{
        uchar sendcmd;   //下位机向PC发送的命令码
           switch (retval)
    {
        case 1:      //数据发送错误,请求PC重发
          { 
             sendcmd=2;  //向PC发送的重发数据命令,PC识别后向下位机重发数据包。
             Send(0xce);
                         Send(0x7b);
                         Send(sendcmd);
                         Send(0xed);  //向PC发送命令

                         retval=0;   //标志清零,防止重复扫描,重复执行。  2013/9/24
                         break;

          }
        case 2:      //数据发送成功,执行命令
          {
             switch (cmd)    //命令解码
                         {
                                 case 0x01:
                                {
                                        Send(0xce);
                                        Send(0x7b);
                                        Send(0x11);
                                        Send(0xed);
                                        cmd=0x00;
                                        break;
                                }
                                case 0x02:
                                {
                                        Send(0xce);
                                        Send(0x7b);
                                        Send(0x12);
                                        Send(0xed);
                                        cmd=0x00;
                                        break;
                                }
                                case 0x03:
                                {
                                        Send(0xce);
                                        Send(0x7b);
                                        Send(0x13);
                                        Send(0xed);
                                        cmd=0x00;
                                        break;
                                }                 
                         }
            }
                  retval=0;   //标志清零,防止重复扫描,重复执行。
     }
}


/*
* 函数名:Send
* 描  述:串口数据发送函数
* 输  入:sendcmd - 待发送的数据
* 输  出:无
* 备  注:
*/
void Send(uint8 sendcmd)
{
   ES=0;  //关闭串口
   SBUF=sendcmd;  //发送数据,向PC发送。
   while(!TI);
   TI=0;  //发送完成,TI清零
   ES=1;  //开串口 
}

以上是我写的这个程序,希望大家指点一下。
程序运行整体可以,但是有个问题,也希望大神们能帮忙看一下什么问题
每次在单片机关机后,再重新上电后,发送都没反应,只有手动按下开发板的复位键后才能正常通信,当再次断电上电后,又不行了,又得按复位键才正常。

关键字:单片机  PC机  多机通讯 引用地址:实现单片机与PC机多机通讯的程序

上一篇:单片机心率测量仪源代码
下一篇:单片机驱动收音机模块程序

推荐阅读最新更新时间:2024-03-16 14:05

MSP432™ MCU的一些关键特性
在降低设计功耗的过程中,您是否充分利用了微控制器(MCU)中集成模数转换器(ADC)的所有功能?这篇博文将带您了解如何借助集成模数器实现更低的功耗。 在这篇博文中,我们将以MSP432P401R MCU中的ADC14(集成14位模数转换器)作为示例。低功耗应用,以及减少高占空比应用中的启动时间都是ADC14设计过程中的考量要素。然而,各个应用都有独特的特点,因此,为最大限度地降低功耗,必须谨慎选择ADC14的旋钮或可编程性。 这篇博文重点讲述MSP432™ MCU的一些关键特性,您可以通过这些特性自定义ADC14的功率和性能: 可选参考 快速启动 可选时钟源 电源模式 最低电压1.62V 使用集成DC / DC驱动核心电压
[单片机]
基于Q2403A的单片机短消息收发系统
引言 基于GSM短消息的业务不需要建立拨号连接,只需把待发的消息加上目的地址发送至短消息中心,再由短消息中心转发到最终目标。GSM 短消息业务以其连接简单、费用低廉、覆盖范围广、实现方便等优点得到了广泛的应用。运用 GSM 短消息实现远程测控的可靠性较高、信号传播距离远、覆盖面积广,并且可以节省建网初期的巨额投资。 本文对基于GSM短消息收发系统的设计与实现作了具体描述,给出了系统的软硬件设计方案,对主要硬件,即GSM模块Q2403A 和8051单片机作了重点介绍。给出了系统的软件设计,包括PC与单片机通信部分和短消息收发部分。最后实现系统监控功能。 系统硬件实现 总体系统结构 该系统硬件主要由8051单片机扩展电路、
[应用]
51单片机的主要组成结构分析详解
随着微电子技术的快速发展,以ARM为主的32位MCU(微控制器)已普及开来,8位MCU已被很多人认为将被淘汰,更何况其中的老古董MCS-51系列单片机。但从目前的形势来看,8位MCU还牢牢占据着工业控制领域的主导地位,一个原因是8位MCU的开发成本比较低,也有大量的成熟设计方案,还有一个原因是历史的延续,新的产品还处在不断变化中,老产品则是经数十年的淘汰而留存下来的精品。 MCS-51系列单片机正是如此,自1980年由Intel推出后,获得很大成功,并不断改进而形成系列,成为最普遍使用的单片机内核和指令系统。后来,ATMEL、NXP等多家著名半导体公司推出兼容和增强的51系列单片机,应用普遍,因此成为单片机教学的主要示例,熟悉其
[单片机]
【51单片机】 蜂鸣器发声程序
蜂鸣器分为有源和无源,这个源是震荡源。 有源的直接给高电平就可以响(也有低电平驱动)。 无源的还需要通过给一个持续到震荡源才能作用。 51单片机开发板上的蜂鸣器通常是无源的。以下是蜂鸣器发声程序。 #include reg51.h sbit beep = P1^5; //定义P1_5为蜂鸣器端口 void delay(unsigned int i) //延时程序 { while(i--); } void main() //主程序 { while(1) //while无限循环 { beep = ~beep; //蜂鸣器程序取反 delay(100); } } 注意:如果程
[单片机]
【51<font color='red'>单片机</font>】 蜂鸣器发声程序
基于单片机的通用示波器存储功能扩展设计
1 引言   目前,通用二踪示波器如 HH4310A/HH4311A 、 RS8 等均无存储功能,在学生实验中能满足信号测量的要求,但若用于测量一些非周期单脉冲信号,由于信号的突发性,这些通用的示波器往往不能对信号的波形、幅值、脉宽进行仔细的观测。其在通用示波器中嵌入存储功能,能极大地扩展应用范围,具有较高的实用价值。笔者介绍一种利用 SPCE061A 型 16 位单片机在 HH4310A/HH4311A 型通用示波器中嵌入存储功能的原理及实验结果。 2 通用示波器的基本工作原理   通用示波器的频率繁多,电路各不相同,但总的来说,可以归纳为 3 个主要组成部分:垂直系统(主要实现 Y 输入信号的放大
[缓冲存储]
如何对单片机读出目标代码进行反汇编
要正确获取程序的目标代码,首先要明确程序代码的存放地点。51单片机的程序存储器最大空间为64KB,在一个实际的应用系统中,程序存储器的分布情况可能有以下几种: (1)只使用了片内程序空间。而没有使用片外的程序空间。 其硬件特征为:/EA引脚接VCC;/PSEN引脚为空脚。 这种情况比较简单,全部应用程序都在单片机内部的程序存储器中,我们只要使用编程器将程序代码读出来,保存为一个目标代码文件就可以了。要注意的是,有一些新型的单片机具有加密功能,如果进行了加密,其中的程序代码就是不能读出。 (2)没有使用片内程序空间,片外程序空间由单个存储芯片构成。 其硬件特征为:/EA引脚接GND;/PSEN引脚接到一个存储芯片上。 这种情况下,全
[单片机]
如何对<font color='red'>单片机</font>读出目标代码进行反汇编
超越自我,逐梦全球|RT-Thread开发者大会圆满落幕!
2021年12月18日,由 睿赛德科技 主办的2021 RT-Thread 开发者大会在深圳精彩收官。作为国内备受瞩目的AIOT行业年度技术盛宴,本届大会以“Beyond”为主题,通过主题演讲、分论坛、技术展示等活动,为广大开发者带来了一场别开生面的盛会。虽在疫情当下,仍有超过800位开发者来到了现场,20000+用户观看直播。 本次大会由嵌入式专家、嵌入式系统联谊会秘书长何小庆主持,何小庆老师在主持中说到:开源是RT-Thread团队的基因,商业是正在发展探索之路。我们一起来回顾RT-Thread开发者的大会的精彩时刻 。 RT-Thread 创始人熊谱翔发表了开场演讲,回顾了2021年AIOT市场情况,总结
[嵌入式]
超越自我,逐梦全球|RT-Thread开发者大会圆满落幕!
基于8051增强型单片机的RJM8L系列超低功耗MCU介绍
对于一些采用电池供电的产品需要长达数年不换电池情况下能连续工作,系统低功耗设计就尤为重要。MCU微控制器的低功耗设计决定系统的成败关键。MCU微控制器的低功耗技术涉及到软件、系统和底层的硬件工艺等。对于应用来说,在空闲的时候,可以将其时钟关闭以节省动态功耗,或小部分电路以低速低功耗的方式运行,SRAM的读写动态功耗相当可观,因此应该尽量减少读写SRAM。 瑞纳捷针对低功耗应用推出了RJM8L151S和RJM8L003系列产品,已大量应用到各领域,如:烟雾报警器,LoRa模组,智能门锁,灯控设备,GPS定位器,安防探测器,电子烟等应用领域。 RJM8L151S和RJM8L003系列产品是基于8051增强型单片机,工作电压2.
[单片机]
基于8051增强型<font color='red'>单片机</font>的RJM8L系列超低功耗<font color='red'>MCU</font>介绍
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved