精华内容
下载资源
问答
  • 串口接收一帧数据解析

    万次阅读 多人点赞 2018-05-25 11:28:16
    3. 下位机中的数据接收和协议解析 下位机接收数据也有两种方式,、等待接收,处理器一直查询串口状态,来判断是否接收到数据。二、中断接收。两种方法的优缺点在此前的篇关于串口通信的文章中详细讨论过。得出...

    3. 下位机中的数据接收和协议解析
        下位机接收数据也有两种方式,一、等待接收,处理器一直查询串口状态,来判断是否接收到数据。二、中断接收。两种方法的优缺点在此前的一篇关于串口通信的文章中详细讨论过。得出的结论是采用中断接收的方法比较好。
        数据包的解析过程可以设置到不同的位置。如果协议比较简单,整个系统只是处理一些简单的命令,那么可以直接把数据包的解析过程放入到中断处理函数中,当收到正确的数据包的时候,置位相应的标志,在主程序中再对命令进行处理。如果协议稍微复杂,比较好的方式是将接收的数据存放于缓冲区中,主程序读取数据后进行解析。也有两种方式交叉使用的,比如一对多的系统中,首先在接收中断中解析“连接”命令,连接命令接收到后主程序进入设置状态,采用查询的方式来解析其余的协议。
        以下给出具体的实例。在这个系统中,串口的命令非常简单。所有的协议全部在串口中断中进行。数据包的格式如下:
    0x55, 0xAA, 0x7E, 0x12, 0xF0, 0x02, 0x23, 0x45, SUM, XOR, 0x0D
        其中0x55, 0xAA, 0x7E为数据帧的帧头,0x0D为帧尾,0x12为设备的目的地址,0xF0为源地址,0x02为数据长度,后面接着两个数据0x23, 0x45,从目的地址开始结算累加、异或校验和,到数据的最后一位结束。
        协议解析的目的,首先判断数据包的完整性,正确性,然后提取数据类型,数据等数据,存放起来用于主程序处理。代码如下:
    if(state_machine == 0)       // 协议解析状态机
    {
          if(rcvdat == 0x55)       // 接收到帧头第一个数据
              state_machine = 1;
          else
              state_machine = 0;      // 状态机复位
    }
    else if(state_machine == 1)
    {
          if(rcvdat == 0xAA)       // 接收到帧头第二个数据
              state_machine = 2;
          else
              state_machine = 0;      // 状态机复位
    }
    else if(state_machine == 2)
    {
          if(rcvdat == 0x7E)       // 接收到帧头第三个数据
              state_machine = 3;
         else
              state_machine = 0;      // 状态机复位
    }
    else if(state_machine == 3)
    {
          sumchkm = rcvdat;       // 开始计算累加、异或校验和
          xorchkm = rcvdat;
          if(rcvdat == m_SrcAdr)      // 判断目的地址是否正确
              state_machine = 4;
          else
              state_machine = 0;
    }
    else if(state_machine == 4)
    {
          sumchkm += rcvdat;
          xorchkm ^= rcvdat;
          if(rcvdat == m_DstAdr)      // 判断源地址是否正确
              state_machine = 5;
          else
              state_machine = 0;
       }
    else if(state_machine == 5)
    {
          lencnt = 0;            // 接收数据计数器
          rcvcount = rcvdat;        // 接收数据长度
          sumchkm += rcvdat;
          xorchkm ^= rcvdat;
          state_machine = 6;
    }
    else if(state _machine == 6 || state _machine == 7)
    {
          m_ucData[lencnt++] = rcvdat;     // 数据保存
          sumchkm += rcvdat;
          xorchkm ^= rcvdat;
          if(lencnt == rcvcount)      // 判断数据是否接收完毕
              state_machine = 8;
          else
              state_machine = 7;
    }
    else if(state_machine == 8)
    {
          if(sumchkm == rcvdat)      // 判断累加和是否相等
              state_machine = 9;
          else
              state_machine = 0;
    }
    else if(state_machine == 9)
    {
          if(xorchkm == rcvdat)      // 判断异或校验和是否相等
              state_machine = 10;
          else
              state_machine = 0;
    }
    else if(state_machine == 10)
    {
          if(0x0D == rcvdat)       // 判断是否接收到帧尾结束符
          {
              retval = 0xaa;    // 置标志,表示一个数据包接收到
          }
          state_machine = 0;     // 复位状态机
    }

        此过程中,使用了一个变量state_machine作为协议状态机的转换状态,用于确定当前字节处于一帧数据中的那个部位,同时在接收过程中自动对接收数据进行校验和处理,在数据包接收完的同时也进行了校验的比较。因此当帧尾结束符接收到的时候,则表示一帧数据已经接收完毕,并且通过了校验,关键数据也保存到了缓冲去中。主程序即可通过retval的标志位来进行协议的解析处理。
        接收过程中,只要哪一步收到的数据不是预期值,则直接将状态机复位,用于下一帧数据的判断,因此系统出现状态死锁的情况非常少,系统比较稳定,如果出现丢失数据包的情况也可由上位机进行命令的补发,不过这种情况笔者还没有碰到。 
    展开全文
  • 关于STM32串口3的使用,接收并解析一帧数据 当stm32的串口1被使用时,我们可以使用其他串口来使用。 步骤: 串口3定义、初始化; 串口3中断服务函数(接收的一帧数据并判断是否正确); 主函数使用; 首先,我们要将...

    关于STM32串口3的使用,接收并解析一帧数据

    当stm32的串口1被使用时,我们可以使用其他串口来使用。

    步骤:

    1. 串口3定义、初始化;
    2. 串口3中断服务函数(接收的一帧数据并判断是否正确);
    3. 主函数使用;

    首先,我们要将串口3初始化并加上中断:

    1. 我这里使用的是STM32F103C8T6的芯片,串口3对应的引脚是PB10、PB11
      引脚

    2. 程序如下:

    /**********************************************************************************
      *函数名              :USART3_Config
      *函数功能            :USART3初始化
      *输入                :无
      *输出                :无   
    
    *******************************************************************************/
    void USART3_Config(void)
    {
    GPIO_InitTypeDef GPIO_InitStructure;     //GPIO初始化结构体声明
    USART_InitTypeDef USART_InitStructure;   //串口初始化结构体声明
    NVIC_InitTypeDef    NVIC_InitStructure;  //NVIC初始化结构体声明
    	
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);      //使能PB端口时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);    //USART3时钟
    
    	
    //USART3的Tx---GPIO----PA.10----复用推挽输出
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    	
    //USART3的Rx---GPIO----PA.11----浮空输入
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
    	
    NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;             //中断通道为USART3_IRQn
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;     //设置抢占优先级2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;            //设置响应优先级2 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;               //通道使能
    NVIC_Init(&NVIC_InitStructure);                      //调用NVIC_Init()函数完成端口初始化
    	
    USART_InitStructure.USART_BaudRate=9600;             //设置波特率为9600
    USART_InitStructure.USART_WordLength=USART_WordLength_8b;                      //数据位8位
    USART_InitStructure.USART_StopBits=USART_StopBits_1;                           //停止位1位
    USART_InitStructure.USART_Parity=USART_Parity_No;                              //无校验位
    USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;  //无硬件流控
    USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;   //接受和发送模式都打开
    USART_Init(USART3,&USART_InitStructure);	        //调用USART_Init()函数完成端口初始化
    
    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);      //使能接收中断
    
    USART_Cmd(USART3,ENABLE);                           //使能串口3
    
    }
    
    

    注意:

    上面程序中使能时钟函数要分开,毕竟RCC_APB2Periph_GPIOB与RCC_APB1Periph_USART3是不一样的,

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);      //使能PB端口时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);    //USART3时钟
    

    而在串口1使能时钟时,却可以放在一起,比如:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
    //使能PA端口时钟、USART1时钟
    

    这个一定要注意,不然程序编译是检测不出的。

    接下来,我们就要码串口中断函数了

    1. 在中断函数里,我们可以接收到数据,并进行处理。我这里是用串口助手的hex来发送数据给STM32,故STM32接收到的数据是一个字节一个字节的。

    下面的函数是接收到数据(一个字节),并判断帧头(0xFF)是否正确,再存放在数组data(十六进制)中,继续接收数据(一个字节)存放在数组data中,接收满四个字节就结束,重新循环判断。其中COM3_RecvFin 是接收一帧数据成功的标志,主函数可以用这个来检验是否接收成功,再进行数据处理。

    void USART3_IRQHandler(void) //串口1中断服务程序
    {
    	u8 shujv;
    	if(USART_GetFlagStatus(USART3,USART_IT_RXNE) != RESET)  //接收到数据
    	{			
    		shujv=USART_ReceiveData(USART3);
    		
    		if((shujv == 0xFF)  &&  (h == 0)){h=1; data[0]=shujv; }
    		else if(h == 1){h=2; data[1]=shujv; }
    		else if(h == 2){h=3; data[2]=shujv; }
    		else if(h == 3){ COM3_RecvFin = 1;  h=0;data[3]=shujv; }
    		else h=0;
    	
    		USART_ClearITPendingBit(USART3,USART_IT_RXNE);        //清除接收标志位,每次接收完成都需要清除一下
    		while(USART_GetFlagStatus(USART3,USART_FLAG_TC) ==RESET);	  //用于检查串口UART1是否发送完成,完成时,TC中断标志置位,退出轮询等待!	
    	}
    }
    
    

    上面的方法是接收到固定长度的数据,当我们不知道数据有多长,只有帧头0xFF、帧尾0xEE,应该怎么办呢?下面就是另一种方法:

    如果检测数据的帧头是0xFF,就将Start_Receiving置1,到后面判断Start_Receiving,再将数据存在数组data中;检测到数据的帧尾是0xEE时就结束并将COM3_RecvFin置1。

    这种方法虽然可以接收不定长度的数据,但是我们的数据(不包括帧头、帧尾)出现0xEE时,数据接收就会提前结束。

    void USART3_IRQHandler(void)
    {  	     
      if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET);//接收到数据		
      {
    		shujv = USART_ReceiveData(USART3);			
    		if(shujv==0xFF)
    		{
    			Start_Receiving=1;
    		}
    		else if(shujv == 0xEE)
    		{
    			COM3_RecvFin=1;
    			Start_Receiving=0;
    			i=0;
    		}
    				
    		if(Start_Receiving==1)
    		{
    			data[i]=shujv;
    			i++;
    		}
    		USART_ClearITPendingBit(USART3,USART_IT_RXNE);        //清除接收标志位,每次接收完成都需要清除一下
    		while(USART_GetFlagStatus(USART3,USART_FLAG_TC) ==RESET);	  //用于检查串口UART1是否发送完成,完成时,TC中断标志置位,退出轮询等待!				
    	}										
    }
    

    有利有弊,两种方法你会选什么呢?

    最后,我们来码主函数了

    主函数,我这里只是码简单的,易于理解,后期能理解的天才可以添加的其他程序,使之更完美!

    int main(void)
    {
    	USART3_Config();                       //USART初始化函数
    	while(1)                               //死循环,永为真。所以用中断函数来中断
    	{
    		if(COM3_RecvFin==1)               //判断到COM3_RecvFin标志为1,表示接收成功 
        	{
                COM3_RecvFin=0;                //COM3_RecvFin标志清零,防止死循环
                USART_SendData(USART3,data[1]);//发送数组data的第2个字节,这里简单发送,可以添加自己需要的程序   
            }		
    	}
    }
    
    

    手痒的,想要试试的,记得开头添加下面的头文件、函数声明、变量定义

    /*---------头文件---------*/
    #include "stm32f10x.h"
    
    /*---------函数声明---------*/
    void USART3_Config(void);
    void USART3_IRQHandler(void);
    u8 COM3_RecvFin=0;
    u8 Start_Receiving=0;
    u8 i=0;
    unsigned  char data[10];
    

    有什么遗漏、错误,请在下面的评论写,一起交流交流。

    展开全文
  • 当STM32的USART1被占用,又需要串口通讯时,可以采用USRAT3的通讯,通讯常常避免不了要判断一帧数据是否为我们需要的,故在程序添加如何解析判断一帧数据(帧头+数据+帧尾),并发送数据
  • 前言 大家好,我是川楠,前段时间在芯吧客的问答频道上回答了好几个关于串口数据帧解析的问题。想想当初我学习单片机的时候,也在这个地方折腾了不少时间,看过了很多的代码,也尝试了多种写法。 实现串口数据帧...

    前言
       大家好,我是川楠,前段时间在芯吧客的问答频道上回答了好几个关于串口数据帧解析的问题。想想当初我学习单片机的时候,也在这个地方折腾了不少时间,看过了很多的代码,也尝试了多种写法。
       实现串口数据帧断帧,有很多的方法,比如使用串口的IDLE中断进行断帧,使用定时器根据时间断帧、使用特殊标识符进行断帧等等,刚开始工作的时候,我自己就写好几个版本,基本上一个项目一个版本,但是现在我使用的基本上只有这一个版本。

    方法一:
       使用串口的IDLE中断:比如STM32有串口的IDLE中断,在数据发送完成,串口进入空闲的时候会产生一个IDLE中断,在HAL中也使用到这种方式。但是,其他的单片机不一定有IDLE中断,比如51单片机、MSP430等等,所以这个方式并不是万能的,.
    方法二:
    使用特殊字符:给数据包的帧头和帧尾使用一个或者一组特定的字符,也就是我们说的帧头和帧尾。这个有一定的概率会造成数据包错位或者数据包内容和帧头尾内容一样。如果数据帧帧尾丢失,着会这造成单片机一直等待数据帧帧尾,进入死等情况。
    方法三:
       使用串口+定时的方式进行断帧。定时器主要用来计时数据帧与帧之间的时间,这要这个时间大于一个设定的阈值,超出这个时间就表示当前数据帧发送完毕,下一次来的数据,一定是一个新的数据帧。
    本次主要对方法三做一个说明。
    这也是我在项目中常用一种方式,这种方式可以适用于串口、RS485、RS422等通讯接口,其带有循环缓存队列的思想用在CAN总线通讯中也十分合适(CAN通讯不需要自己去判断报文的帧头帧尾),这样的通讯设置方式能够极大的降低数据掉包率。

    使用资源
       串口     1个
       定时器    1个

    实现基本原理
    采用循环队列,将串口的数据流进行接收,根据定时器计时单个BYTE之间的时间,只要这个时间超过一个阈值,则认为下一个数据为新的数据帧开头。(使用循环缓存队列进行数据接收、采用定时器进行数据断帧),
    数据接收采用的是单字节中断接收方式。在查串口的中断函数中,只进行数据接收,不对数据进行处理。在主函数中,才对数据帧的合法性进行判断和数据帧内容进行处理和应答。

    特点
    采用循环缓存数据队列,有效的降低数据掉包风险
    适用于串行通讯
    要求发送数据必须连续,且帧与帧之间需要有一定的时间间隔。
    数据帧可以实现任意长度
    采用生产消费者模型的设计思路


    下面我就举个自己设计的项目例子吧。本次使用STM32F103RCT6单片机,当然我也将这种方式用到过MSP430、8051、NRF24LE1等单片机中。

    数据帧规定
    一个完整的数据包由开始字符、设备类型码、设备地址码、命令字、数据长度、数据区、CRC校验及结束字符组成。如下所示:

    5965274ab68f4203c3011cb934ef5e58.png

    各区域含义如下:
    开始字符:1字节,表示一个数据包的开始。固定为0xAA。
    设备类型:1字节,表示设备类型,固定为0x01。所有设备都能接收广播类型(0xFF)的命令请求
    设备地址:1字节,表示设备在系统中的地址,由用户可以自行定义,默认值为0x01。所有设备都能接收广播地址(0xFF)的命令请求。
    命令字:1字节,即功能命令。见下文,按照规定的格式访问设备。
    数据长度:1字节,表示后面跟随的有效数据区的字节数,范围0  -  240。
    数据区:N字节,为有效数据,长度为前面数据长度定义的字节数。数据长度为0时没有数据区。数据区的数值,参见命令描述。
    CRC校验:2字节,从开始字符到数据区最后一个字节的所有字符的16位CRC校验值。校验值低位再前,高位在后。
    结束字符:1字节,表示数据包结束。固定为0x0E。

    数据帧断帧时间阈值
    在发送数据包的时候,要求先准备好所有要发送的字符,连续发送出去,中间不得有较长时间停顿。
    多个数据包间必须有3.5个BYTE传送时间的时间间隔。
    例:RS232传输位如下:
    1 起始位
    8 数据位,首先发送最低有效位
    0 位作为奇偶校验
    1 停止位

    计算时间:T=3.5*( 1+数据位+奇偶校验+停止位) / 波特率
    如:使用9600,则间隔时间为4ms。
    在波特率大于19200的时候,使用固定的时间1.75ms

    eab8403dba28f0f5a82b77603b27a95d.png

    注意:既然定义了这样的时间间隔,那么就必须要要求发送端发过来的数据帧也要满足这个时间间隔的标准。

    eg:约定总线的波特率为115200,如果发送端需要发送3个数据帧的时候,那么就必须要要求这个三个数据帧与帧的时间间隔必须要要大于1.75MS,否者接收端就会把这三个数据帧认为是一个数据帧。

    程序代码实现
    首先需要对串口初始化:将单片机的串口配制成实际要求的串口波特率,和通讯格式,我这里默认配置为115200,8,N,1的方式。串口要打开接收中断,每接收一个BYTE数据,就要进入中断一次。
    定时器也需要进行初始化,将定时的时间间隔为50uS,并且启动定时器,让定时器间隔50uS进入中断函数一次。
    串口与定时器初始化的代码我就不公布了,这是单片机开发人员的常规操作。而且每款单片机的初始化的方式不一样。

    接下来就进入主题了,在进入主题之前,先介绍下我所定义的一些结构体类型,不然后面看起来会相当的吃力:
    首先,定义了一个T_FramCtlType的数据结构体。这个结构体用来记录帧与帧之间的时间间隔。主要是用在定时器的中断函数中,这里大家有个相关的映象即可:

    24c729d47273d72f5ebe0804801ba4ce.png

    然后我定义了一个消息和循环缓存的数据结构体:

    853de46a690b061eee45978acf332db8.png

    ComMsgType单个数据帧的最大长度,我这里定义为为250个字节,大家在使用的时候,可以根据自己的协议设计要求,可以自行定义。
    MsgFifoType是串口消息的循环缓存队列和帧时间的一个结构体,可以缓存10个数据帧,后面我们所有的操作都是围绕着这个结构体进行操作。
       接下来是协议相关的,我这里宏定义了数据帧的帧头,帧尾和数据帧类容的结构体。

    a537ea2faa29e99321b66d536201168a.png

    下面开始看看代码的编写,首先是结构体数据的初始化。

    942bf0eb0ecb1e642a5298f0c4297525.png

    注意:COMMsgFifo[_COMx]这个是我为了适配多路串口同时使用,所以定义了多个MsgFifoType类型的缓存,一路串口一个COMMsgFifo缓存池。所以这里需要稍微把思路转换下......

    点击下方“
    展开全文
  • //每接收一帧数据,记得把定时计数清零,相当于是看门狗,但是在定时中断里面会不断累加。 if(TI) { TI=0; } } } void clr_recvbuffer(unsigned char *buf) { unsigned char i; for(i=0;i { buf[i]=0; } }

    #include

    #include

    #include

    #define max_rev_time  5

    #define rev_cnt       10

    #define max_rev_num   20

    unsigned char recv_buf[max_rev_num];

    unsigned char start_timer=0;

    unsigned char recv_timer_cnt;

    unsigned char recv_cnt;

    unsigned char recv_flag=0;

    void Delay100ms();

    void sendbyte(unsigned char dat);

    void sendstring(unsigned char *dat1);

    char putchar(char c);

    void Timer0Init(void);

    void UartInit(void);//9600bps@11.0592MHz

    void clr_recvbuffer(unsigned char *buf);

    void uart_service(unsigned char *buf);

    sbit led=P1;

    void main()

      {

       P1=0XFF;

    Timer0Init();

      UartInit();

    EA=1;

      printf("wait for serial communication test start.\r\n");

    printf("please send a string of data: \r\n");

       while(1)

    {

    uart_service(recv_buf);

    }

       }

    void uart_service(unsigned char *buf)

    {

    unsigned char recv_move_index;

    if(recv_flag)

    {

    recv_flag=0;

    start_timer=0;//关定时器

     sendstring(buf);//处理数据

    while((recv_cnt>=5)&&(recv_move_index<=recv_cnt))

    {

    if((buf[recv_move_index+0]==0x55) &&(buf[recv_move_index+1]==0xAA)&&(buf[recv_move_index+2]==0x55))

    {

    if((buf[recv_move_index+3]==0x01) &&(buf[recv_move_index+4]==0x02))

    {

    led=0X00;

    }

    if((buf[recv_move_index+3]==0x02) &&(buf[recv_move_index+4]==0x01))

    {

    led=0XFF;

    }

    break;

    }

    recv_move_index++;

    }

       recv_cnt=0;

    clr_recvbuffer(recv_buf);//清除缓冲BUFFER

    }

    }

    void Delay100ms() //@11.0592MHz

    {

    unsigned char i, j, k;

    _nop_();

    _nop_();

    i = 5;

    j = 52;

    k = 195;

    do

    {

    do

    {

    while (--k);

    } while (--j);

    } while (--i);

    }

    void UartInit(void) //9600bps@11.0592MHz

    {

    PCON |= 0x80; //使能波特率倍速位SMOD

    SCON = 0x50; //8位数据,可变波特率

    //AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T

    //AUXR &= 0xFE; //串口1选择定时器1为波特率发生器

    TMOD &= 0x0F; //清除定时器1模式位

    TMOD |= 0x20; //设定定时器1为8位自动重装方式

    TL1 = 0xFA; //设定定时初值

    TH1 = 0xFA; //设定定时器重装值

    ET1 = 0; //禁止定时器1中断

    ES=1;

    TR1 = 1; //启动定时器1

    }

    void Timer0Init(void) //1毫秒@11.0592MHz

    {

    //AUXR &= 0x7F; //定时器时钟12T模式

    TMOD &= 0xF0; //设置定时器模式

    TMOD |= 0x01; //设置定时器模式

    TL0 = 0x66; //设置定时初值

    TH0 = 0xFC; //设置定时初值

    TF0 = 0; //清除TF0标志

    ET0 =1;

    TR0 = 1; //定时器0开始计时

    }

    void timer0_isr() interrupt 1

    {

      TR0=0;

    if(start_timer==1)

    {

    recv_timer_cnt++;//累加定时器时间计数器

    if(recv_timer_cnt>max_rev_time)//判断定时时间表是否超过了设定的最大的阀值,超过则说明等待一段时间后没有新的数据到,我们判断一包数据接收完毕。

    {

    recv_timer_cnt=0;//清除定时计数器 处理数据  清除BUFFER (放到数据处理之后。

    //recv_cnt=0;

    recv_flag=1;

    }

    }

    TL0 = 0x66; //设置定时初值

    TH0 = 0xFC; //设置定时初值

    TR0=1;

    }

    void sendbyte(unsigned char dat)

    {

      SBUF=dat;     //10位  异步串口通信 0  88H  1

    while(!TI);

    TI=0;

    }

    void sendstring(unsigned char *dat)

    {

    while(*dat!='\0')

      {

       sendbyte(*dat++);

      }

    }

    char putchar(char c)

    {

      sendbyte(c);

    return c;

    }

    void uart_isr() interrupt 4

      {

      if(RI)

      {

       RI=0;

    start_timer=1;//每接收一帧数据的时候,打开软件定时器,去计数。

    if(recv_cnt < max_rev_num)

    {

    recv_buf[recv_cnt]=SBUF;//接收数据到数据缓冲区,注意缓冲区的大小范围问题

    recv_cnt++;

    }

    else

    {

      recv_cnt=max_rev_num;

      }

      recv_timer_cnt=0;//每接收一帧数据,记得把定时计数清零,相当于是看门狗,但是在定时中断里面会不断累加。

    if(TI)

    {

       TI=0;

      }

      }

      }

    void clr_recvbuffer(unsigned char *buf)

    {

    unsigned char i;

    for(i=0;i

    {

    buf[i]=0;

    }

    }

    1cd4f1aecca004edc7e7ae29920ed1a3.png

    展开全文
  • 前言 大家好,我是川楠,前段时间在芯吧客的问答频道上回答了好几个关于串口数据帧解析的问题。想想当初我学习单片机的时候,也在这个地方折腾了不少时间,看过了很多的代码,也尝试了多种写法。 实现串口数据帧...
  • 解析串口-接收完整数据帧

    万次阅读 2016-05-23 16:48:35
    但是我们会发现,read次得到的数据通常不是完整的数据帧。比如完整数据帧为但是实际上需要read多次才能完全读到。程序实际运行情况:两次读完:四次读完:为了解决不能接收完整数据帧的问题,借鉴了网友的例子...
  • 嵌入式系统中,关于... 关于串口传输,个人觉得采用modbus协议来接受数据是比较合理的,采用3.5char字符的超时机制,接受的时候如果判断超时,就当作一帧数据进行处理,所以这种情况,帧格式没有那么讲解,发送...
  • java串口通信之帧解析

    千次阅读 2017-05-03 21:56:38
    串口通信解包是比较复杂的,解包的情况...而这种想法考虑太欠缺了,如果不知道发送方发送的是否是完整的一帧数据或者是否存在正确的数据, 那么首先应该对收到的一串数据进行搜帧,判断有几个首尾为7E的字段,然后分别
  • 串口完整帧数据接收的实现方式

    万次阅读 多人点赞 2018-08-23 10:21:28
    本人采用的STM32HAL库,部分函数为库函数提供,其中硬件初始化反初始化函数部分需要自己实现,这里不给出实现代码,数据帧接收实现方式基本通用于所有串口通信,以下是实现的代码。 附:如果你想使用字符串形式的...
  • 串口-帧解析

    千次阅读 2018-06-14 00:26:46
    测试条件:stm32不停的向PC发送数据0x22,发送先发送低位,校验位为奇校验,1.5个停止位一帧数据解析见下图:通过上图可以看到如下信息:1:起始位:02:数据位:0100 0100 (地位在前,高位在后),代表的数值为0x...
  • C 解析串口数据

    千次阅读 2019-07-09 17:07:52
    最近面试,收到份面试题,特此研究下C的串口解析 题目要求如下: 用C语言写个程序,此程序持续从串口读取数据(串口速率是115200,偶校验),串口数据有可能包含DL-T 645协议格式或者DL-T 698协议格式的数据帧...
  • 这个必须要将数据进行缓存,因为一次来的数据可能并不是你所期望的一帧数据,可能多,也可能少,同时可能由于某种原因,一帧数据没有完,后一帧的数据又来了,会导致数据错乱,所以也要确定一帧数据到达的时间,超过...
  • 串口数据解析总结

    2018-03-27 19:21:00
    但是我们会发现,read次得到的数据通常不是完整的数据帧。 比如完整数据帧为 但是实际上需要read多次才能完全读到。 程序实际运行情况: 两次读完: 四次读完: 为了解决不能接收完整数据帧的问题...
  • 嵌入式串口同步数方法解析 串口通信是日前单片机和DSP等嵌入式系统之间,以及嵌入式系统与PC机或无线模块之间的种非常重要且普遍使用的通信方式。在嵌入式系统的硬件结构中,通常只有个8位或16位的CPU,不仅...
  • 树莓派3b接收USB串口数据解析处理

    千次阅读 2020-08-02 16:39:46
    我们在这里定义串口数据的帧头为 0x3A 0x3B,帧尾为 0x7E 0x7F,一帧最大的长度为 10个字节。 帧头帧尾格式可自行更改。 // 最大帧长度 #define MaxFrameLength 10 // 对最大帧长度加以限定,防止接收到过长的...
  • STM32F103C8T6开发笔记整理​zhuanlan.zhihu.com【写在前面】:这篇笔记的内容,是要实现STM32中串口的收发功能,二是要解析来自上位机发送的命令,从而控制单片机实现一些功能。通过上位机发送的不同的命令,控制...
  • Labview串口数据接收和解析程序设计 1.基本需求 下位机按固定的频率上传数据数据帧包含头和尾,尾的长度以两个字节为例,上传的数据被上位机接收并存储在缓冲区,labview程序对缓冲区的数据进行周期性的...
  • STM32F103C8T6开发笔记整理​zhuanlan.zhihu.com【写在前面】:这篇笔记的内容,是要实现STM32中串口的收发功能,二是要解析来自上位机发送的命令,从而控制单片机实现一些功能。通过上位机发送的不同的命令,控制...
  • 这是个ros的serial接收/发送节点,基于ros::serial::Serial类。 从串口读取的数据理论上无任何丢失。 imu信息是按照采用sensor_msgs/Imu+MagneticField的消息进行解析;...time帧数据是用来进行rtt时间同步的帧。
  • FPGA串口通信要想应用在实际的工业现场,需要整套完整的协议,来确保数据传输的可靠性和系统的稳定性。基于协议,进行串口指令解析是控制的关键,对于串口指令解析,有两种方式:逻辑解析和软硬核(我用的Altera的...
  • 这里以串口作为传输媒介,介绍下怎样来发送接收个完整的数据包。过程涉及到封包与解包。...、根据尾或者长检测数据帧 1、头+数据+校验+尾 这是个典型的方案,但是对头与

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 164
精华内容 65
关键字:

串口解析一帧数据