资讯详情

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

下面是我写的一个实现多个下位机(单片机)与一个上位机(PC机)的一主多从串口通讯程序,用的C89C52RC,定时器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 * 描 述:通信协议解析函数 * 输 入:无 * 输 出:无 * 备 注:解析串口接收到的数据 /*-------------------------------- 多机通信协议格式 ------------------------------------*/ /* 数据包的格式如下所示(共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=0; //发送完成,TI清零 ES=1; //开串口 }

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

-电子元器件采购网(www.ruidan.com)是本土元器件目录分销商,采用“小批量、现货、样品”销售模式,致力于满足客户多型号、高质量、快速交付的采购需求。 自建高效智能仓储,拥有自营库存超过50,000种,提供一站式正品现货采购、个性化解决方案、选型替代等多元化服务。
锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台