精华内容
下载资源
问答
  • 之前在做项目的时候,串口接收数据要及时进行处理,虽然采用了自定义的串口协议,但是协议的包尾只有一个字节,经常判断不准数据是否接受完毕,所以就采用计时+串口的方式来判定串口是否接受完成。 核心思想 ...
    使用背景:

    之前在做项目的时候,串口接收的数据要及时进行处理,虽然采用了自定义的串口协议,但是协议的包尾只有一个字节,经常判断不准数据是否接受完毕,所以就采用计时器+串口的方式来判定串口是否接受完成。

    核心思想

    根据波特率来计算接收一个字节所需要的时间,当超过这个时间没有收到数据,则表明这一帧数据已经接受完毕

    实现方法

    串口中断函数接收第一个字节之后,开启定时器计数。接受下一个字节的时候清空定时器计数。如此,当没有数据接收后,计时器无法清零,当计时器计数超过设定的数值之后,触发定时器溢出中断,此时数据即接收完毕

    Created with Raphaël 2.2.0开始定义 计时标志位、完成标志位接收数据,清空计时标志位开启定时器,计时标志位增加串口未收到数据?计时标志位增加,超过设定值接收完成标志位置1,处理数据结束yesno

    下面是代码:

    • 1、为了方便,定义一个结构体,主要内容如下所示:
    typedef struct _UART_FLAG_STRUCT
    {
    	uint8_t UART1Flag;//数据接受完成标志
    	uint8_t UART1String[160];//最大长度,自定义
    	uint8_t UART1Counter;//收到的数据长度,计数作用
    	uint8_t usart1_start;//接收开始,定时器计时启动
    	uint8_t usart1_counter;//定时器计时次数
    }	UartFlagSt;
    
    UartFlagSt UartFlagStC;
    
    • 串口配置:
    /************************************************
    | 关键词 |USARTInit(u32 band)
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |u32 band,配置波特率
      - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |None
      - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |初始化串口1,使能中断
    **************************************************/
    void USARTInit(u32 band)
    {
    	GPIO_InitTypeDef  GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    	NVIC_InitTypeDef  NVIC_InitStructure;
    	
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO,ENABLE);
    	
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Pin  =GPIO_Pin_9;//send
    	GPIO_Init(GPIOA,&GPIO_InitStructure);
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//receive
    	GPIO_Init(GPIOA,&GPIO_InitStructure);
    	
    	USART_InitStructure.USART_BaudRate = band;
    	USART_InitStructure.USART_HardwareFlowControl = 0;
    	USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
    	USART_InitStructure.USART_Parity = USART_Parity_No;
    	USART_InitStructure.USART_StopBits =USART_StopBits_1;
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    	USART_Init(USART1,&USART_InitStructure); //初始化串口1
    
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
    	
    	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启串口接受中断
    	USART_Cmd(USART1,ENABLE);                    //使能串口1 
    }
    
    /************************************************
    | 关键词 |UART1_SendString(uint8_t* str,uint8_t counter)
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |uint8_t* str    发送的数据
    |	|uint8_t counter 发送数据的长度
      - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |无
      - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |串口1发送函数
    **************************************************/
    void UART1_SendString(uint8_t* str,uint8_t counter)
    {
    	for(int i = 0;i<counter;i++)
    	{
    		USART_SendData(USART1,*str);
    		while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
    		str++;
    	}
    }
    
    • 定时器配置:
    /************************************************
    | 关键词 |TIM2_Init(u16 arr,u16 psc)
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |u16 arr 周期,u16 psc 预分频
    | - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |无
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |定时器初始化
    **************************************************/  
    void TIM2_Init(u16 arr,u16 psc)
    {
    	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	NVIC_InitTypeDef NVIC_InitStructure;
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	
    	TIM_TimeBaseStructure.TIM_Period = arr; //	周期 72-1 max
    	TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频 
    	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); 
    	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE );
    
    	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
    	NVIC_Init(&NVIC_InitStructure);
    
    	TIM_Cmd(TIM2, ENABLE);
    }
    
    • 串口中断函数:
    /************************************************
    | 关键词 |USART1_IRQHandler
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |None
      - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |None
      - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |1、消除错误;
    |	|2、将数据接受至结构体中的数,
    |       |并启动UartFlagStC.usart1_start = 1;UartFlagStC
    |       |.usart1_counter = 0;定时/刷新功能。
    **************************************************/
    void USART1_IRQHandler(void)
    {
    /*****此段注释代码为消除各种串口错误,在串口环境很差的情况下可以直接将此段代码取消注释,基本可以保证串口代码正常工作******
    	if((USART_GetITStatus(USART1,USART_IT_ORE) == SET)||(USART_GetFlagStatus(USART1,USART_FLAG_ORE) == SET))
    	{
    		USART_ReceiveData(USART1);
    		USART_ClearITPendingBit(USART1,USART_IT_ORE);
    		USART_ClearFlag(USART1,USART_IT_ORE);
    	}
    	
    	if((USART_GetITStatus(USART1,USART_IT_NE) == SET)||USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET)
    	{
    		USART_ClearITPendingBit(USART1,USART_IT_NE);
    		USART_ClearFlag(USART1, USART_FLAG_NE);
    	}
    
    	if((USART_GetITStatus(USART1,USART_IT_FE) == SET)||USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET)
    	{
    		USART_ClearITPendingBit(USART1,USART_IT_FE);
    		USART_ClearFlag(USART1, USART_FLAG_FE);
    	}
    
    	if((USART_GetITStatus(USART1,USART_IT_PE) == SET)||USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET)
    	{
    		USART_ClearITPendingBit(USART1,USART_IT_PE);
    		USART_ClearFlag(USART1, USART_FLAG_PE);
    	}
    ****************************************************************************************************/
    	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    	{
    		if(UartFlagStC.UART1Counter<160)//设定的数组最大为160,要小于这个数,防止溢出
    		{
    			UartFlagStC.UART1String[UartFlagStC.UART1Counter] = USART_ReceiveData(USART1);//将数据存到数组里面
    			UartFlagStC.UART1Counter++;//收到的数据个数+1
    			UartFlagStC.usart1_start = 1;//定时器开始工作
    			UartFlagStC.usart1_counter = 0;//清空定时器计数
    		}else UartFlagStC.UART1Counter = 0;//如果接受的数据超过设定值,则清空接收值,防止数据溢出
    		USART_ClearITPendingBit(USART1,USART_IT_RXNE);
    	}
    }
    

    可以看到,当有数据进来的时候,UartFlagStC.UART1Counter不断自增,数据将会依次存入UartFlagStC.UART1String[]数组中。
    同时,UartFlagStC.usart1_start计时器开始计数标志位置一(让在定时器中断函数里面自增的UartFlagStC.usart1_counter得以正常增加),同时也将UartFlagStC.usart1_counter清零,以表示有数据接收,防止超过设定值,使得UartFlagStC.UART1Flag置一,错误的提示数据提前接收完成。

    • 定时器中断函数:
      在下面代码提示插入任务的地方插入我们想执行的任务/代码,即可正常使用
    /************************************************
    | 关键词 |TIM2_IRQHandler
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |无
    | - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |无
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |定时器中断函数
    **************************************************/  
    void TIM2_IRQHandler(void)
    {
    	if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    	{
    		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    		/**********************/
    		 在此处插入执行的任务。
    		/**********************/
    	}
    }
    
    • 关键代码:
      这个代码是我们任务重最重要的部分,主要实现了我们上述流程图的计时/完成任务,之所以将这部分功能单独拆解写成一个函数的形式,主要是为了移植方便,并且在定时器多任务的时候让定时器中断函数看起来更整洁一点。
    使用方法:将此函数插入定时器中断函数。
    /************************************************
    | 关键词 |USART1InsetTimer
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |None
      - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |无
      - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |嵌入定时器中断函数中,串口数据接收完毕后
    |		 |立刻执行,适用于延时性小、数据量密的场景。
    **************************************************/
    void USART1InsetTimer(void)
    {
    	if(UartFlagStC.usart1_start == 1)//此标志位是在串口接收数据时候会置1
    	{
    		UartFlagStC.usart1_counter++;//定时器计数标志位
    		if(UartFlagStC.usart1_counter >USART_COUNTER_9600)//如果超过波特率为9600时一个字节的所需要的时间,时间计算方法下面有讲解
    		{
    			UartFlagStC.UART1Flag = 1;//接收完成标志位置1
    			UartFlagStC.usart1_counter = 0;//计数值清零
    			UartFlagStC.usart1_start = 0;//计数器启动标志位置0
    		}
    	}
    }
    

    在这里,当(UartFlagStC.usart1_start置一后,UartFlagStC.usart1_counter会不断自增(串口中断中会清零此计数位),而一旦超过设定值USART_COUNTER_9600,就会将接收完成标志位UartFlagStC.UART1Flag置一,同时清空定时器技术位UartFlagStC.usart1_counter,并清零计数允许标志位UartFlagStC.usart1_start

    • 处理数据:
      如果处理数据很快的话可以直接放在定时器中断函数里面执行,如果还有比较长的延时函数,或者在执行过程中花费时间太久则可以放入主函数循环中进行处理:
      老规矩,先进行封装一层:
    /************************************************
    | 关键词 |USART1Hanndle
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |None
      - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |无
      - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |串口1接收数据处理函数,任务若花费时间较长,
    |        |可放置于while()循环中,由定时器确定是否执行。
    **************************************************/
    void USART1Hanndle(void)
    {
    	if(!UartFlagStC.UART1Flag) return;
    	/*********执行任务*************/		
    	printf("%s\r\n",UartFlagStC.UART1String);
    	/****************************/
    	memset(UartFlagStC.UART1String,0,160);
    	UartFlagStC.UART1Counter = 0;
    	UartFlagStC.UART1Flag = 0;
    }
    

    主函数实现:

    /************************************************
    | 关键词 |main
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  入参  |None
    | - - - - - - - - - - - - - - - - - - - - - - - - 
    | 返回值 |无
    | - - - - - - - - - - - - - - - - - - - - - - - -
    |  功能  |主函数入口,配置文件,设置时钟,滴答定时器
    |        |周期,并开启看门狗
    **************************************************/  
    int main()
    {
    	SystemInit();   				 	//系统时钟72MHz
    	SysTick_Config(SystemCoreClock/1000);        /* SysTick 1 msec interrupts */
    	TIM2_Init(71,999);//1ms中断一次
    	USARTInit(9600);//波特率为9600
    	while(1)
    	{
    		USART1Hanndle();
    	}
    }
    

    流程/细节讲解

    • 不同波特率延时时间计算:

    可能会有人对void USART1InsetTimer(void)中的溢出时间USART_COUNTER_9600有疑问,不知道如何计算,计算方法如下:

    首先,1个字符串口包含起始位数据位校验位停止位,其中有些位长度可以自己设定,
    这里我们按一个字节传输有1+8+1+1共10位长度来计算。
    波特率表示的意思是在1sec内可以传输的位数


    接下来就是一元一次方程


    设1个字节所用时间为X,波特率为9600,则:

    (110)/X=9600/1000(ms)(1 * 10) / X = 9600 / 1000(ms)
    解得X ≈ 1.04167 ms = 2(X为整型,必须向上取整!)

    X代表的意思是一帧数据传输的时间,意思就是每过X单位时间,即有一个数据接受完毕,同时下一个数据也即将接受。
    USART_COUNTER_9600的数值设定为X1
    UartFlagStC.usart1_counter则只要在串口中断函数内清空,那么UartFlagStC.usart1_counter就不会超过X,
    那么也就不会将接受完成标志位UartFlagStC.UART1Flag置1;
    一旦没有数据继续接收,那么UartFlagStC.usart1_counter在中断函数里面将不断自增,直至超过X,此时接受完成标志位UartFlagStC.UART1Flag将会置1。
    同时,时间设定要根据波特率的不同要计算不同的数值。

    下载地址 :https://download.csdn.net/download/qq_31431301/12287318


    1. 实际使用过程中,一定要将USART_COUNTER_9600的设定值大于X,因为在此方法中,串口中断函数不仅要判断数据接收标志位,还有清零置一的操作,实际工作时间肯定要大于X!,一般取3~5倍X的时间。 ↩︎

    展开全文
  • 串口数据接收处理

    千次阅读 2017-08-17 18:17:49
    串口接收函数只需要管理数据的接收就行了,不必管理相应数据接收的是什么,还有顺序对不对,真正寻找数据的地方是在数据处理的地方。void USART2_IRQHandler(void) { if(USART_GetITStatus(USART2, USART_IT_RXNE) !...

    串口接收函数只需要管理数据的接收就行了,不必管理相应数据接收的是什么,还有顺序对不对,真正寻找数据的地方是在数据处理的地方。

    void USART2_IRQHandler(void)
    {
    
        if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) 
        {
            if(esp8266_cnt >= sizeof(esp8266_buf))  esp8266_cnt = 0; 
            esp8266_buf[esp8266_cnt++] = USART2->DR;
    
            USART_ClearFlag(USART2, USART_FLAG_RXNE);
        }
    
    }

    数据处理端,函数来自于OneNET麒麟板程序

    void OneNet_App(void)
    {
    
        char *dataPtr;
        char numBuf[10];
        int num = 0;
        unsigned char *cmd;
    
        cmd = (unsigned char *)strchr((char *)esp8266_buf, ':');
        if(cmd == NULL)    
            return;
    
        cmd++;
        if(cmd[0] == 0xA0)                                  
            OneNet_Replace(cmd, 4);     
        else
            return;
    
        dataPtr = strstr((const char *)cmd, "}");           //
    
        if(dataPtr != NULL)                                 //
        {
            dataPtr++;
    
            while(*dataPtr >= '0' && *dataPtr <= '9')       //
            {
                numBuf[num++] = *dataPtr++;   //
            }
    
            num = atoi((const char *)numBuf);               //
    
            if(strstr((char *)cmd, "redled"))               //
            {
                if(num == 1)                                //
                {
                    Led5_Set(LED_ON);
                }
                else if(num == 0)                           //
                {
                    Led5_Set(LED_OFF);
                }
            }
                                                            //
            else if(strstr((char *)cmd, "greenled"))
            {
                if(num == 1)
                {
                    Led4_Set(LED_ON);
                }
                else if(num == 0)
                {
                    Led4_Set(LED_OFF);
                }
            }
            else if(strstr((char *)cmd, "yellowled"))
            {
                if(num == 1)
                {
                    Led3_Set(LED_ON);
                }
                else if(num == 0)
                {
                    Led3_Set(LED_OFF);
                }
            }
            else if(strstr((char *)cmd, "blueled"))
            {
                if(num == 1)
                {
                    Led2_Set(LED_ON);
                }
                else if(num == 0)
                {
                    Led2_Set(LED_OFF);
                }
            }
        }
    
        ESP8266_Clear();                                    //
    
    }
    展开全文
  • C#编写的串口数据接收保存

    热门讨论 2014-12-08 15:19:50
    本压缩文件是一个完整的C#编写的串口采集软件,可以实时接收串口数据并显示在文本框内,以Excel表格的形式实时记录储存数据,含有全部代码和界面设计等,也可直接使用.本人采用的是vs2010,若有下载后不能使用的请给我...
  • 基于Linux平台的串口数据接收源程序,可以直接在Linxu平台下进行功能测试。
  • 串口数据接收处理的一种方法

    千次阅读 2019-09-11 16:14:13
    不管是在微控制中使用串口还是在桌面应用中使用串口,都需要解决这个接收的数据包处理的问题。最近在做一个应用,主要是上位机发送指令,查询下位机相关的数据。假设一帧数据是由10字节构成的,指令和应答数据均为...

    在处理串口接收到的数据时,面临的问题主要是是如何接收到完整的数据,对于数据如何处理等。不管是在微控制器中使用串口还是在桌面应用中使用串口,都需要解决这个接收的数据包处理的问题。最近在做一个应用,主要是上位机发送指令,查询下位机相关的数据。假设一帧数据是由10字节构成的,指令和应答数据均为10字节,应答数据发送频率为5Hz。有两个问题需要解决,第一个就是数据帧什么时候接收完成,第二个接收完成如何处理。
    针对第一个问题,我的方法是,利用一个定时器来监控数据是否接收完成,接收一个数据就重新装载定时器的计数值,定时时间应该在ms级别(10个字节也可能会多发或少发),假设是115200的波特率,那么10个字节的数据时间大概在0.9ms ,考虑到可能会有阻塞,可以定时5ms,5ms没有接收到新的数据,就说明接收已经完成了,颗粒有点像看门狗的思路。这个问题解决后,就要解决第二个问题了。我的上位机使用Qt来做的,给出一个代码,代码中用到的数据需要在头文件中定义:

    /*read data*/
    void Widget::readSerialData(void)
    {
    	timerMonitorSerial->start(5);
    	QByteArray buf;
    	buf=serialPort->readAll();
    	for(unsigned char index=0;index<buf.size();index++)
    	{
    		receiverQueue.enqueue(buf.at(i)&0xFF);
    	}
    	buf.clear();
    }
    /*time out,receive complete*/
    void Widget::serialDataResolution()
    {
    	timerMonitorSerial->stop();
    	int executionCount=0;
    	executionCount=receiverQueue.size();
    	for(quint8 index=0;index<executionCount;index++)
    	{
    		serialPortProtocolResolution(receiverQueue.dequeue());
        }
        receiverQueue.clear();
    }
    

    对于第二个问题,假设数据帧针头为head,帧尾为tail ,第九个数据是第二个数据到第八个数据的累加和的低字节,我的处理方式是先对帧头进行判断,如果遇到了帧头,则进入到下一个数据的接收,然后一直接收8个数据,进行校验,校验通过,进行下一个状态,判断帧尾对不对,如果对了 ,给出一个信号,可以进行数据的处理了。这个思路就像是一个状态机,因为之前学过FPGA,写过状态机,后面写这个的时候就想到了这个方法。

    void Widget::serialPortProtocolResolution(unsigned char const buf)
    {
    	unsigned char index=0;
    	unsigned shor int sum=0;
    	static unsigned char receiveCounter=0;
    	static unsigned char stateMachine=0;
    	if(0==stateMachine)
    	{
    		if(head==buf)
    		{
    			staticMachine=1;
    			memset(validData,0,10);
    			validData[0]=buf;
    		}
    		else 
    		{
    			staticMachine=0;
    		}	
        }
        else if(1==stateMachine)
    	{
    		validData[receiveCounter]=buf;
    		receiveCounter++;
    		if(8==receiveCounter)
    		{
    			receiveCounter=0;
    			stateMachine=2;
    		}
        }
        else if(2==stateMachine)
        {
    		for(index=1;index<8;index++)
    		{
    			sum+=validData[index];
    		}
    		if(validData[8]==buf)
    		{
    			stateMachine=3;
    		}
    		else
    		{
    			stateMachine=0;
     		}
        }
        else if(3==stateMachine)
        {
        	if(tail==buf)
    		{
    			validData[9]=buf;
    			stateMachine=0;
    			emit getTrueDataSignal();
            }
            else
            {
    			stateMachine=0;
            }
        }
    	else
    	{
    	    stateMachine=0;
    	}
    }
    

    思路大概就是这样,在数据接收的时候,这里使用的是软件定时器,如果是单片机的话,可以使用硬件定时器,串口中断配置成一个字节中断一次,定时器超时进入中断认为接收完成。

    展开全文
  • HAL库教程6:串口数据接收

    万次阅读 多人点赞 2019-04-09 22:50:01
    STM32的串口接收机制   与阻塞式发送函数HAL_UART_Transmit配套,有个阻塞式的接收函数,HAL_UART_Receive,但此函数不常用,串口接收通常使用中断函数HAL_UART_Receive_IT。HAL库的串口中断比较复杂,主要流程...

    STM32的串口接收机制

      与阻塞式发送函数HAL_UART_Transmit配套,有个阻塞式的接收函数,HAL_UART_Receive,但此函数不常用,串口接收通常使用中断函数HAL_UART_Receive_IT。HAL库的串口中断比较复杂,主要流程如下:
    在这里插入图片描述
      USART1_IRQHandler:由硬件调用,不是HAL库函数,寄存器编程或固件库编程也需要调用此函数;
      HAL_UART_IRQHandler:通过中断类型(发送中断还是接收中断)来判断调用哪个函数;
      UART_Receive_IT:此函数可以指定,每收到若干个数据,调用一次回调函数;这是因为,每收到一个字节,都会把此函数的接收计数器-1,如果接收计数器为零,调用串口接收回调函数HAL_UART_RxCpltCallback(实际上HAL库一共提供了5个回调函数,只有这个函数在接收完成时调用)。
      HAL_UART_RxCpltCallback:弱函数,用户可以在此函数中编写业务逻辑。清除中断标记,是中断处理函数一定要做的事情,但是对于用户函数,把这个操作给隐藏了

    使能串口接收中断

      由于串口不方便传参数,所以我通常会定义一些用于串口通信的全局变量。也可以模仿库函数,把这些变量打包成一个结构体。

    //UART.c
    unsigned char UART1_Rx_Buf[MAX_REC_LENGTH] = {0}; //USART1存储接收数据
    unsigned char UART1_Rx_flg = 0;                   //USART1接收完成标志
    unsigned int  UART1_Rx_cnt = 0;                   //USART1接受数据计数器
    unsigned char UART1_temp[REC_LENGTH] = {0};       //USART1接收数据缓存
    
    

      由于这些变量也要在main.c文件中使用,跨文件使用,可以在头文件中做外部声明:

    #ifndef __UART_H
    #define __UART_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
      
    #define REC_LENGTH  1
    #define MAX_REC_LENGTH  1024 
      
    extern unsigned char UART1_Rx_Buf[MAX_REC_LENGTH];
    extern unsigned char UART1_Rx_flg ;
    extern unsigned int  UART1_Rx_cnt ;
    extern unsigned char UART1_temp[REC_LENGTH];
        
    #ifdef __cplusplus
    }
    #endif
    
    #endif 
    
    

      要使用中断来接收串口数据,则必须开启中断。并且,每次处理完串口接收中断以后,会自动关闭中断,如果想循环接收数据,则必须在处理完中断以后,再次开启中断。
      我们希望完成初始化以后就开始接收串口数据,所以要修改串口初始化函数。

    //main.c
    static void MX_USART1_UART_Init(void)
    {
      /* USER CODE BEGIN USART1_Init 2 */
      HAL_UART_Receive_IT(&huart1,(uint8_t *)UART1_temp,REC_LENGTH);
      /* USER CODE END USART1_Init 2 */
    }
    
    

    串口接收中断函数处理

      程序的逻辑:
    在这里插入图片描述
      如果接收到了指定数量的串口数据(在本例中,指定的数量是1字节),则会执行回调函数HAL_UART_RxCpltCallback。此函数是个弱函数,用户可以根据业务逻辑来“重载”。我们要在此函数中,把串口收到的数据打包,并判断结束符判断数据结束。我们规定,只发送ASCII码,并以0x0a作为结束符。

    //UART.c
    /**
      * @brief 串口中断回调函数
      * @param 调用回调函数的串口
      * @note  串口每次收到数据以后都会关闭中断,如需重复使用,必须再次开启
      * @retval None
      */  
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      if(huart->Instance==USART1)
      {
        UART1_Rx_Buf[UART1_Rx_cnt] = UART1_temp[0];
        UART1_Rx_cnt++;
        if(0x0a == UART1_temp[0])
        {
          UART1_Rx_flg = 1;
        }
        HAL_UART_Receive_IT(&huart1,(uint8_t *)UART1_temp,REC_LENGTH);
      }
    }
    

      主函数中,实现“串口应声虫”的功能,收到什么就发送什么。如果串口数据接收完成,则发送出去然后把数组,计数器,标志都恢复初始状态。

    //main() while(1)
        if(UART1_Rx_flg)
        {
          HAL_UART_Transmit(&huart1,UART1_Rx_Buf,UART1_Rx_cnt,0x10);    //发送接收到的数据
          for(int i = 0;i<UART1_Rx_cnt;i++)
            UART1_Rx_Buf[i] = 0;
          UART1_Rx_cnt = 0;
          UART1_Rx_flg = 0;
        }   
    
    

      现象:向串口发送ASCII码,单片机收到什么数据,就返回什么数据。注意,发送给串口的数据结尾要有回车键。
    在这里插入图片描述

    数据阶段的方法与ASCII码

      一组数据怎么判断是否结束?
      2种方法:
      1特定时间,特定的时间内没有收到新的数据,认为这一组数据就结束了。这种方法在定时器的章节来实现。
      2特定字符,通信双方约定,用特定的字符作为结束,比如把0xff作为结束符。收到0xff就把数据截断。就像我们演讲,最后说一句谢谢大家,下边的人就知道了你讲完了,该鼓掌了。谢谢就是结束符。
      但是这种做法有一个弊端,就是正常通信的数据不允许在使用0xff。
      对于ASCII码,正常情况下是不会发送0x0d与0x0a(回车与换行)的,所以可以用作结束符。
      ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是现今最通用的系统,并等同于国际标准ISO/IEC 646。
      ASCII码主要用于英文字符的显示,不包含中文。标准ASCII码只有7位(最高位是校验位),所以只能显示2^7=128个字符,其中0-31还是不能显示的字符,例如回车,作用是控制字符或通信字符。
    在这里插入图片描述
      假如,发送的数据是0x31,它可能代表着十六进制的数字0x31,也可能表示十进制的数字49(十六进制与十进制虽然看上去不一样,但表示的数是同样的大小),还可能表示ASCII码,字符’1’。这三者,在传输线上使用示波器来观察,波形是一模一样的,接收方把它理解为0x31还是理解为字符,要看通信双方的约定。

    2020年11月27日更新

    有读者反馈不好用,这段代码是经过测试,确认可用的,附上源码如下,供参考
    https://download.csdn.net/download/geek_monkey/13195359
    另外,此串口数据需要结束符,后续文章中有更好的方法,定时器截断,文章地址是:
    https://yatao.blog.csdn.net/article/details/89326199

    展开全文
  • 万能的串口数据接收及处理方法

    万次阅读 多人点赞 2018-12-14 10:17:34
    接下来就分享一下本人在项目中使用串口接收数据及处理的方法。  先介绍下主要思路,首先需要开一个定时器,我们接收串口端发来的数据,这个时候会产生接收中断,在中断里面我们做三件事,1.启动定时器;2.清除时间...
  • Labview串口数据接收和解析程序设计 1.基本需求 下位机按固定的频率上传数据,数据帧包含帧头和帧尾,帧头帧尾的长度以两个字节为例,上传的数据被上位机接收并存储在缓冲区,labview程序对缓冲区的数据进行周期性的...
  • STM32串口数据接收 --环形缓冲区

    千次阅读 2021-02-01 11:35:57
    STM32串口数据接收 --环形缓冲区 环形缓冲区简介   在单片机中串口通信是我们使用最频繁的,使用串口通信就会用到串口的数据接收与发送,环形缓冲区方式接收数据可以更好的保证数据丢帧率第。   在通信程序中,...
  • STM32之串口DMA接收不定长数据

    万次阅读 多人点赞 2018-09-17 15:49:25
    STM32之串口DMA接收不定长数据 本文为杰杰原创,如需转载请说明出处 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? ...
  • Matlab串口数据接收发送流程和串口速度提高

    万次阅读 多人点赞 2018-09-11 19:28:52
    在进行PID参数整定的工作过程中,我...在整个过程中采用了Matlab来进行串口数据的读写。目前整个流程已经走通,在此将实现过程中遇到的问题,解决方法,以及尚未解决的疑问进行总结。 首先声明本人是一个初学M...
  • GPS串口数据接收程序实例

    千次阅读 2008-02-21 21:56:00
    GPS串口数据接收程序实例摘 要:目前GPS(全球定位系统)定位应用市场日趋成熟,正在进入应用的高速发展时期。本文以一款EverMore公司的GM-X205GPS接受模块为例,介绍了其数据格式,以及应用PIC16F874单片机RS232...
  • STM32串口发送数据接收数据方式总结

    万次阅读 多人点赞 2018-05-13 20:34:22
    之前写了篇关于ESP8266使用AT指令进行互相通讯的实验,在写STM32串口接发数据的程序中,觉得有必要将之前学的有关于串口方面的使用经历加以总结。 串口发送数据: 1. 串口发送数据最直接的方式就是标准调用...
  • 这种方法可以有效解决串口数据接收发生换行的问题,并且解决CH340 / CP2102 多硬件显示结果不一致的问题。那有没有别的方式?各有什么优劣? 串口数据接收不发生换行,的几种解决方法: 第1种:...
  • 前提:有一个项目一直使用DMA+串口空闲中断接收数据,最近发现串口数据丢包。 经过检查发现串口发送机在发送数据过程中本来应该是连续发送,但是中间有某一个字节与下一个字节之间的间距大于一个字节,MCU端会认为...
  • C#代码基本如下: private List<byte> buffer = new List<byte>(4096);  void comm_DataReceived(object sender, ...// 如果正在关闭,忽略操作,直接返回,尽快的完成串口监听...
  • 原因 C++ 字符型char一个字节位8位二进制,最高位为符号位,1表示负数,0表示正数。所以它表示的范围是 -127~128。而当我们接收的是FF时就超过了它所表示的范围...用unsigned char 类型来接收串口传递过来的数据。 ...
  • C语言串口程序接收数据

    千次阅读 2019-12-10 09:46:54
    串口接收数据以TXT文档保存,如何让程序运行时让文本中只保存此次接收数据 `这是我的代码 #include <Windows.h> #include <stdio.h> HANDLE hCom;//HANDLE 调度消息,将一个任务切换到某个指定的...
  • 认识MATLAB 串口 串口对象的属性 使用Serial函数为指定串口创建一个串口对象。调用格式为: obj = serial('port'); 采用默认属性,创建一个与指定串口相关联的串口对象,并返回该串口对象的句柄。若串口port不存在...
  • 按照协议匹配,避免串口数据接收时顺序换乱错误。 包头 长度 地址码 回复状态 校验和 包尾 备注 C0C0 02 F5 AA YY CF 成功 ...
  • LabVIEW上位机串口数据接受不完整、不正常的解决方法 通常在测试、测量实验中, 使用LabVIEW做上位机的开发,串口通信是一种常用的数据传输方式。 1在传输数据较少、波特率低的情况下,通常的串口接受数据不会存在...
  • 利用STM32F103ZET6的串口3接收ESP8266的数据,通过串口1打印出来。
  • 由于项目需要,用到了5个串口,在使用的过程中遇到了几个问题: 一、串口4和串口5无法进入接收中断,这个问题在网上已经找到了答案,这里也说明一下,是因为我的工程里面...二、有一路串口没有接收数据他却能一直...
  • STM32双串口接收数据

    2021-05-16 15:04:05
    工程要求需要至少两个串口,一个串口用于接收传感器数据,另一个串口用于接收上位机或者遥控发来的指令,本次开发的软件使用的是CubeMX和keil5的编译环境,使用的单片机是STM32F103RCT6,使用串口3接收传感器的...
  • 关于串口数据的发送和接收(调试必备)

    万次阅读 多人点赞 2019-03-21 20:44:53
    对于串口数据发送和接收,大多是都是利用串口中断来进行的,但是这样对于编程方面有一定要求,并且程序也不太好写,比如说,如果让你随意接收一段数据,然后利用串口将它发送出来,第一个需要考虑的问题就是接收...
  • MFC 动态绘制曲线 适用于串口数据接收显示

    万次阅读 多人点赞 2018-03-30 10:07:49
    计时Timer 数组左移 基于Timer的绘图 任何界面库都会有Timer这个实现,在MFC中时OnTimer消息,在Qt中是QTimer类,那种原理基本都一样,下面将以MFC(VC)为例进行说明。 Timer是消息级别最低的消息,它会保证其它...
  • STM32如何利用串口发送接收数据

    千次阅读 热门讨论 2021-05-08 11:38:49
    STM32如何利用串口发送接收数据? 我现在计划利用STM32F103X的串口对迪文屏发送及接收数据。 手中硬件:正点原子开发板(旗舰版),迪文屏(4.3寸),电脑 软件:MCU程序下载:FLYMCU;串口助手:XCOM;迪文屏配置:...
  • 串口调试助手接收数据画曲线
  • STM32 DMA接收串口数据

    2020-01-03 17:27:48
    通过DMA,无需中断,接收不定时长的串口数据 ...比如四轴飞行,当在不停地获取姿态控制方向时,又要去接收串口数据.答:使用DMA,无需CPU中断便能实现接收串口数据 1.DMA介绍 DMA,全称为: Direct Memory Access...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 47,186
精华内容 18,874
关键字:

串口数据接收软件