找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5269|回复: 16
收起左侧

单片机串口如何接收上位机不断发送的数据帧,并将该帧的有效数据提取出来?

  [复制链接]
ID:49578 发表于 2018-8-16 17:29 | 显示全部楼层 |阅读模式
各位大虾:本人菜鸟请教一个问题,单片机如何接送串口不断发送的数据帧,并将该帧的有效数据提取出来,请赐教一个完整程序。
注:我用的是89C52,通信115200已经调试成功,我的思路是先接收一完整帧判断,然后再进行帧数分析,该帧为02 14 30 57 00 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 17 03 ,帧头为02,帧尾03,我要提取的数据是64。
回复

使用道具 举报

ID:213173 发表于 2018-8-16 20:21 | 显示全部楼层
给你一个PM2.5空气质量检测模块的串口测试示例参考,TX-1C实验板。
//AA 00 10 00 4B 5B FF  用串口助手模拟PM2.5空气质量检测模块发送的数据
#include <reg51.h>
#define uint unsigned int
#define uchar unsigned char
sbit dula=P2^6;                        //段选
sbit wela=P2^7;                        //位选
uchar code table[]={                //共阴数码管0~F数组
        0x3f,0x06,0x5b,0x4f,
        0x66,0x6d,0x7d,0x07,
        0x7f,0x6f,0x77,0x7c,
        0x39,0x5e,0x79,0x71};
uchar table0[] ="OK\n";        //用于串口助手返回验证
uchar table1[]="ERROR\n";//用于串口助手返回验证
uchar rec_buf[7];                        //缓存
uint V_data;                                //收到的16位有效数据变量
uint swan,wan,qian,bai,shi,ge;//数码管显示位
bit flag=0;                                        //接收完成标
/*************初始化串口**************/
void InitUART()                //9600bps@11.0592MHz
{
        PCON &= 0x7F;                //波特率不倍速
        SCON = 0x50;                //8位数据,可变波特率
        TMOD= 0x20;                        //设定定时器1为8位自动重装方式
        TL1 = 0xFD;                        //设定定时初值
        TH1 = 0xFD;                        //设定定时器重装值
        ET1 = 0;                                //禁止定时器1中断
        TR1 = 1;                                //启动定时器1
        EA = 1;                                //开总中断
        ES = 1;                                //开串口中断
}
/**********串口发送函数*************/
void SendOneByte(uchar c)
{
    SBUF = c;                //发送数据
    while(!TI);        //等待发送完成
    TI = 0;                        //发送中断请求标志位清0
}
/************数据解析程序*************/
void analysis()
{
        uchar i,j;                //临时变量
        if(flag==1)                //7位数据串接收完成
        {
                ES=0;                        //关串口中断
                flag=0;                //接收完成标志清0
                j=rec_buf[1]+rec_buf[2]+rec_buf[3]+rec_buf[4];//数据和
                if(rec_buf[5]==j)//验证数据和
                {
                        V_data=(rec_buf[1]<<8)|rec_buf[2];//恢复16位有效数据
                        for(i=0;i<3;i++)
                        SendOneByte(table0[i]);//返回OK
                }
                else for(i=0;i<6;i++)
                        SendOneByte(table1[i]);//返回ERROR
                ES=1;                //开串口中断
        }
}
/*************数据分解***************/
void Transformation()
{
        swan = V_data/100000;                        // 十万位
        wan  = V_data%100000/10000;                // 万位
        qian = V_data%10000/1000;                // 千位
        bai  = V_data%1000/100;                        // 百位
        shi  = V_data%100/10;                        // 十位
        ge   = V_data%10;                                        // 个位
}
/**********6位数码管显示程序************/
void display()
{
        static uchar k=0;                                //分时显示变量
        P0=0x00;                                                //消隐
        dula=1;
        dula=0;
        switch(k)
        {
                case 0:
                        P0=0x7e;                                //显示十万位码
                        wela=1;
                        wela=0;
                        P0=table[swan];                        //显示十万位段码
                        dula=1;
                        dula=0;
                        k++;
                 break;       

                case 1:
                        P0=0x7d;                                //显示万位位码
                        wela=1;
                        wela=0;
                       
                        P0=table[wan];                        //显示万位段码
                        dula=1;
                        dula=0;
                        k++;
                 break;       

                case 2:
                        P0=0x7b;                                //显示千位位码
                        wela=1;
                        wela=0;
                       
                        P0=table[qian];                        //显示千位段码
                        dula=1;
                        dula=0;
                        k++;
                 break;       

                case 3:
                        P0=0x77;                                //显示百位位码
                        wela=1;
                        wela=0;
                       
                        P0=table[bai];                        //显示百位段码
                        dula=1;
                        dula=0;
                        k++;
                 break;       

                case 4:
                        P0=0xef;                                //显示十位位码
                        wela=1;
                        wela=0;
                       
                        P0=table[shi];                        //显示十位段码
                        dula=1;
                        dula=0;
                        k++;
                 break;       

                case 5:
                        P0=0xdf;                                //显示个位位码
                        wela=1;
                        wela=0;
                       
                        P0=table[ge];                        //显示个位段码
                        dula=1;
                        dula=0;
                        k=0;
                 break;       
        }
}
/**************主程序**************/
void main()
{
        InitUART();                //初始化串口
        while(1)
        {
                analysis();        //数据解析
                Transformation();//数据分解
                display();        //数码管显示
        }
}
/*********串口中断服务程序**********/
void UARTInterrupt() interrupt 4
{
        static uchar num=0;                //静态计数变量
        RI=0;                                        //接收中断请求标志位清0
        rec_buf[num]=SBUF;                //接收到的数据串保存在缓存数组
        if(rec_buf[0]==0xAA)        //验证数据头(起始位)
        {
                num++;
                if(num>=7)
                {
                        flag=1;                        //接收完成标志置1
                        num=0;                        //计数变量清0
                }
        }
}

评分

参与人数 1黑币 +50 收起 理由
admin + 50 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:307391 发表于 2018-8-17 08:25 | 显示全部楼层
串口接受数据,把接受的数据存进数组,对比校验的数据是否正确,正确的话取出自己需要的数据

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:387162 发表于 2018-8-17 10:40 | 显示全部楼层
类似状态机的设计也可以吧 就是符合条件才进入下一个状态

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

使用道具 举报

ID:303383 发表于 2018-8-17 23:14 来自触屏版 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

ID:49578 发表于 2018-8-20 15:19 | 显示全部楼层
wulin 发表于 2018-8-16 20:21
给你一个PM2.5空气质量检测模块的串口测试示例参考,TX-1C实验板。
//AA 00 10 00 4B 5B FF  用串口助手模 ...

感谢分享
回复

使用道具 举报

ID:49578 发表于 2018-8-20 15:20 | 显示全部楼层
小猫猫爱吃鱼 发表于 2018-8-17 23:14
枕头和枕尾是固定的吗

是的  帧头,帧尾均固定
                                   02                 //帧头
                                   14                 //数据长度: 长度=命令+参数+效验
                                   30 57 00        //命令
                                   64 00        //找零箱内硬币数
                                   00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                   17                 //XOR :XOR=长度xor命令xor参数
                                   03                 //帧尾   
回复

使用道具 举报

ID:49578 发表于 2018-8-20 15:21 | 显示全部楼层
ux4123 发表于 2018-8-17 10:40
类似状态机的设计也可以吧 就是符合条件才进入下一个状态

请赐教,我不大懂您说的
回复

使用道具 举报

ID:388207 发表于 2018-8-20 16:33 | 显示全部楼层
定好协议,定长发送,定长接收,存放在rxd_buf[]数组中,需要的时候从数组中取出对应的位就行
回复

使用道具 举报

ID:49578 发表于 2018-8-29 11:18 | 显示全部楼层
neuangel 发表于 2018-8-20 16:33
定好协议,定长发送,定长接收,存放在rxd_buf[]数组中,需要的时候从数组中取出对应的位就行

我编写的主程序中LED无法点亮,硬件电路核查过没问题,void Data_anlysize()函数也没问题,Data_ok能收到,不清楚问题在哪?
#include "reg52.h"                         //此文件中定义了单片机的一些特殊功能寄存器

#define uint unsigned int         //对数据类型进行声明定义
#define uchar unsigned char


uchar Data_buf[22];          //串口接收数据缓存数组
uint  Data_recieve[22];      //串口接收到完整帧
uint  flag;                                         //串口接收结束标志
uint  Data_ok;                             //有效数据提取标志

sbit ticket=P1^0;                            //将单片机的P1.0端口定义为乘客购票检测
sbit voice=P1^4;                            //将单片机的P1.4端口定义为语音提示开关
sbit led=P0^0;                     //将单片机的P0.0端口定义bug检测

void UsartInit();            //串口初始化
void Data_anlysize();                 //数据分析

/*******************************************************************************
* 函 数 名       : main
* 函数功能                 : 主函数
* 输    入       : 无
* 输    出             : 无
*******************************************************************************/
void main()
{        
        UsartInit();                   //串口初始化
        while(1)
        {
                voice=0;
            Data_anlysize();            //数据分析
                if(Data_ok==1&&ticket==0)  //判断乘客有购票行为
                {
                         voice=1;              //开语音提示
                         led=0;
                }        
        }               
}
/*******************************************************************************
* 函数名         : Usart() interrupt 4
* 函数功能                 : 串口通信中断函数
* 输入           : 无
* 输出                  : 无
*******************************************************************************/
void Usart() interrupt 4
{
        static uchar count=0;                //静态计数变量
    RI=0;                                //接收中断请求标志位清0
    Data_buf[count]=SBUF;                //接收到的数据串保存在缓存数组
    if(Data_buf[0]==0x02)                //验证数据头(起始位)
    {
        count++;
        if(count>=23)
        {
            flag=1;                        //接收完成标志置1
            count=0;                        //计数变量清0
        }
     }
}
/*******************************************************************************
* 函数名         :UsartInit()
* 函数功能                 :串口初始化
* 输入           : 无
* 输出                  : 无
*******************************************************************************/
void UsartInit()
{
        SCON=0X50;                        //设置为串口工作方式1(SM0=0,SM0=1),允许接收 (REN=1)
        T2CON=0X34;                        //设置T2为波特率发生器工作方式(TCLK=1,RCLK=1,TR2=1),T2为定时器(C_T2=0 )
        //T2MOD=0X00;                        //设置T2为加法计数DCEN=0,时钟输出不使能T2OE=0
        TH2=0XFF;                    
        TL2=0XFD;
        RCAP2H=0XFF;
        RCAP2L=0XFD;            //计数器T2初始值设置,波特率115200
        EA=1;                                //打开总中断
        ES=1;                                //设置中断允许控制寄存器IE,允许串行中断   
}
/*******************************************************************************
* 函数名         :Data_anlysis()
* 函数功能                 :串口协议分析
* 输入           : 无
* 输出                  : 无
* 备注                  : 帧结构
                   02                 //帧头
                                   14                 //数据长度: 长度=命令+参数+效验
                                   30 57 00        //命令
                                   64 00        //找零箱内硬币数
                                   00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                   17                 //XOR :XOR=长度xor命令xor参数
                                   03                 //帧尾      
*******************************************************************************/
void Data_anlysize()
{
        uchar i,j;                //临时变量
        if(flag==1)               //23位数据串接收完成
        {
            ES=0;                 //关串口中断
            flag=0;               //接收完成标志清0
            j=Data_buf[2]^Data_buf[3]^Data_buf[4]^Data_buf[5]^Data_buf[6]^Data_buf[7]^Data_buf[8]^Data_buf[9]^Data_buf[10]^Data_buf[11]^Data_buf[12]^Data_buf[13]^Data_buf[14]^Data_buf[15]^Data_buf[16]^Data_buf[17]^Data_buf[18]^Data_buf[19]^Data_buf[20];//XOR校验
            if(Data_buf[21]==j)  //校验通过
            {
               for(i=0;i<=22;i++)
                                   Data_recieve=Data_buf;//接收完整帧
                           if(Data_recieve[5]==0x64)
                               Data_ok=1;
            }
            ES=1;                //开串口中断
          }
}
回复

使用道具 举报

ID:49578 发表于 2018-8-29 11:19 | 显示全部楼层
ux4123 发表于 2018-8-17 10:40
类似状态机的设计也可以吧 就是符合条件才进入下一个状态

我编写的主程序中LED无法点亮,硬件电路核查过没问题,void Data_anlysize()函数也没问题,Data_ok能收到,不清楚问题在哪?
#include "reg52.h"                         //此文件中定义了单片机的一些特殊功能寄存器

#define uint unsigned int         //对数据类型进行声明定义
#define uchar unsigned char


uchar Data_buf[22];          //串口接收数据缓存数组
uint  Data_recieve[22];      //串口接收到完整帧
uint  flag;                                         //串口接收结束标志
uint  Data_ok;                             //有效数据提取标志

sbit ticket=P1^0;                            //将单片机的P1.0端口定义为乘客购票检测
sbit voice=P1^4;                            //将单片机的P1.4端口定义为语音提示开关
sbit led=P0^0;                     //将单片机的P0.0端口定义bug检测

void UsartInit();            //串口初始化
void Data_anlysize();                 //数据分析

/*******************************************************************************
* 函 数 名       : main
* 函数功能                 : 主函数
* 输    入       : 无
* 输    出             : 无
*******************************************************************************/
void main()
{        
        UsartInit();                   //串口初始化
        while(1)
        {
                voice=0;
            Data_anlysize();            //数据分析
                if(Data_ok==1&&ticket==0)  //判断乘客有购票行为
                {
                         voice=1;              //开语音提示
                         led=0;
                }        
        }               
}
/*******************************************************************************
* 函数名         : Usart() interrupt 4
* 函数功能                 : 串口通信中断函数
* 输入           : 无
* 输出                  : 无
*******************************************************************************/
void Usart() interrupt 4
{
        static uchar count=0;                //静态计数变量
    RI=0;                                //接收中断请求标志位清0
    Data_buf[count]=SBUF;                //接收到的数据串保存在缓存数组
    if(Data_buf[0]==0x02)                //验证数据头(起始位)
    {
        count++;
        if(count>=23)
        {
            flag=1;                        //接收完成标志置1
            count=0;                        //计数变量清0
        }
     }
}
/*******************************************************************************
* 函数名         :UsartInit()
* 函数功能                 :串口初始化
* 输入           : 无
* 输出                  : 无
*******************************************************************************/
void UsartInit()
{
        SCON=0X50;                        //设置为串口工作方式1(SM0=0,SM0=1),允许接收 (REN=1)
        T2CON=0X34;                        //设置T2为波特率发生器工作方式(TCLK=1,RCLK=1,TR2=1),T2为定时器(C_T2=0 )
        //T2MOD=0X00;                        //设置T2为加法计数DCEN=0,时钟输出不使能T2OE=0
        TH2=0XFF;                    
        TL2=0XFD;
        RCAP2H=0XFF;
        RCAP2L=0XFD;            //计数器T2初始值设置,波特率115200
        EA=1;                                //打开总中断
        ES=1;                                //设置中断允许控制寄存器IE,允许串行中断   
}
/*******************************************************************************
* 函数名         :Data_anlysis()
* 函数功能                 :串口协议分析
* 输入           : 无
* 输出                  : 无
* 备注                  : 帧结构
                   02                 //帧头
                                   14                 //数据长度: 长度=命令+参数+效验
                                   30 57 00        //命令
                                   64 00        //找零箱内硬币数
                                   00 00 00 00 00 00 00 00 00 00 00 00 00 00
                                   17                 //XOR :XOR=长度xor命令xor参数
                                   03                 //帧尾      
*******************************************************************************/
void Data_anlysize()
{
        uchar i,j;                //临时变量
        if(flag==1)               //23位数据串接收完成
        {
            ES=0;                 //关串口中断
            flag=0;               //接收完成标志清0
            j=Data_buf[2]^Data_buf[3]^Data_buf[4]^Data_buf[5]^Data_buf[6]^Data_buf[7]^Data_buf[8]^Data_buf[9]^Data_buf[10]^Data_buf[11]^Data_buf[12]^Data_buf[13]^Data_buf[14]^Data_buf[15]^Data_buf[16]^Data_buf[17]^Data_buf[18]^Data_buf[19]^Data_buf[20];//XOR校验
            if(Data_buf[21]==j)  //校验通过
            {
               for(i=0;i<=22;i++)
                                   Data_recieve=Data_buf;//接收完整帧
                           if(Data_recieve[5]==0x64)
                               Data_ok=1;
            }
            ES=1;                //开串口中断
          }
}
回复

使用道具 举报

ID:382067 发表于 2018-8-30 16:34 来自触屏版 | 显示全部楼层
判断起始位,如果符合要求,则设置一个接收开始的标志位,然后直到接收到停止位就可以将接收停止位复位。如果不设立停止位,判断一下接收时间也可以,大概超过30ms就表示下一个数据就是下一帧的了
回复

使用道具 举报

ID:213173 发表于 2018-8-31 19:42 | 显示全部楼层
本帖最后由 wulin 于 2018-9-1 17:39 编辑
zfeng45 发表于 2018-8-29 11:18
我编写的主程序中LED无法点亮,硬件电路核查过没问题,void Data_anlysize()函数也没问题,Data_ok能收到 ...

给你把程序改了,你试试
#include "reg52.h"           //此文件中定义了单片机的一些特殊功能寄存器

#define uint unsigned int    //对数据类型进行声明定义
#define uchar unsigned char

uchar Data_buf[23];          //串口接收数据缓存数组
uint  Data_recieve[23];      //串口接收到完整帧
uint  flag;                  //串口接收结束标志
uint  Data_ok;               //有效数据提取标志

//sbit ticket=P1^0;            //将单片机的P1.0端口定义为乘客购票检测
//sbit voice=P1^4;             //将单片机的P1.4端口定义为语音提示开关
//sbit led=P0^0;               //将单片机的P0.0端口定义bug检测

sbit ticket=P3^4;            //按键开关模拟乘客购票检测
sbit voice=P1^4;             //LED显示模拟语音提示
sbit led=P1^0;               //LED显示

void UsartInit();            //串口初始化
void Data_anlysize();        //数据分析
void delayms(uint k);
/*-------------------------------
  1ms延时子程序
-------------------------------*/
void delayms(uint k)
{
        uint i,j;
        for(i=k;i>0;i--)
                for(j=110;j>0;j--);
}
/*******************************************************************************
* 函 数 名       : main
* 函数功能       : 主函数
* 输    入       : 无
* 输    出       : 无
*******************************************************************************/
void main()
{        
        UsartInit();                   //串口初始化
        while(1)
        {

                Data_anlysize();            //数据解析
                if((Data_ok==1)&&(ticket==0))  //判断乘客有购票行为
                {
                        voice=0;              //开语音提示
                        led=0;                                                //LED亮
/********延时观察LED********************/
                        delayms(1000);                                //延时1秒,
                        voice=1;              //关语音提示
                        led=1;                                                //LED熄
                        Data_ok=0;                                //有效数据提取标志复位
                }        
        }               
}
/*******************************************************************************
* 函数名         : Usart() interrupt 4
* 函数功能                 : 串口通信中断函数
* 输入           : 无
* 输出                  : 无
*******************************************************************************/
void Usart() interrupt 4
{
        static uchar count=0;                //静态计数变量
        uchar i;
        RI=0;                                //接收中断请求标志位清0
        Data_buf[count]=SBUF;                //接收到的数据串保存在缓存数组
        if(Data_buf[0]==0x02)                //验证数据头(起始位)
        {
                count++;
                if(count>=23)
                {
                        if(Data_buf[22]==0x03)                        //验证数据尾(结束位)
                                flag=1;                    //接收完成标志置1
                        else
                                for(i=0;i<23;i++)
                                        Data_buf=0x00;                        //缓存数组清0
                        count=0;                      //计数变量清0
                }
        }
}
/*******************************************************************************
* 函数名         :UsartInit()
* 函数功能       :串口初始化
* 输入           : 无
* 输出           : 无
*******************************************************************************/
void UsartInit()        //晶振频率11.0592MHz
{
        SCON=0X50;      //设置为串口工作方式1(SM0=0,SM0=1),允许接收 (REN=1)
        T2CON=0X34;     //设置T2为波特率发生器工作方式(TCLK=1,RCLK=1,TR2=1),T2为定时器(C_T2=0 )
        //T2MOD=0X00;   //设置T2为加法计数DCEN=0,时钟输出不使能T2OE=0
        TL2 = RCAP2L =0XFD;//计数器T2初始值设置,波特率115200
        TH2 = RCAP2H =0XFF;                     
        EA=1;           //打开总中断
        ES=1;           //设置中断允许控制寄存器IE,允许串行中断   
}
/*******************************************************************************
* 函数名         :Data_anlysis()
* 函数功能       :串口协议分析
* 输入           : 无
* 输出           : 无
* 备注           : 帧结构
                   02           //帧头
                   14           //数据长度: 长度=命令+参数+效验
                   30 57 00     //命令
                   64 00        //找零箱内硬币数
                   00 00 00 00 00 00 00 00 00 00 00 00 00 00
                   17           //XOR :XOR=长度xor命令xor参数
                   03           //帧尾      
*******************************************************************************/
void Data_anlysize()
{
        uchar i,j;                //临时变量
        if(flag==1)               //23字节数据串接收完成
        {
                ES=0;                 //关串口中断
                flag=0;               //接收完成标志清0
                j=0;
                for(i=0;i<20;i++)
                        j=j^Data_buf[i+1];//XOR校验
                if(Data_buf[21]==j)  //校验通过
                {
                        for(i=0;i<23;i++)
                                Data_recieve=Data_buf;//接收完整帧
                        if(Data_recieve[5]==0x64)
                                Data_ok=1;
                        for(i=0;i<23;i++)
                                Data_buf=0x00;                        //缓存数组清0
                }
                ES=1;                //开串口中断
        }
}
//02 14 30 57 00 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 17 03

回复

使用道具 举报

ID:49578 发表于 2018-9-4 08:29 | 显示全部楼层
wulin 发表于 2018-8-31 19:42
给你把程序改了,你试试
#include "reg52.h"           //此文件中定义了单片机的一些特殊功能寄存器

...

非常感谢
回复

使用道具 举报

ID:283332 发表于 2021-12-18 15:41 | 显示全部楼层
我将"给你一个PM2.5空气质量检测模块的串口测试示例参考,TX-1C实验板"的程序作了部分移植,总体没什么问题,在analysis()函数内调用发送 SendOneByte(table0[i]),应放在ES=1 之后
回复

使用道具 举报

ID:883242 发表于 2021-12-18 16:22 | 显示全部楼层
上位机实时性太差了,我定义通讯协议都是用ASCII码的形式,比如冒号是帧头标志,\r\n是帧尾标志,中间数字是'0'~'9','A'~'Z'的可打印字符。

单片机之间的通讯,因为实时性好,可以定成总线空闲3.5字节的时间,下面来的第一个字符就是帧头,不需要特殊字符来区别。
回复

使用道具 举报

ID:811253 发表于 2021-12-18 16:23 | 显示全部楼层
一般都是把数据封装成具有一定格式的数据包来发送,收发双方按协议打包或是解释数据。
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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