串口通信数据收发处理流程

本文介绍了STM32串口数据接收的两种常见方法:基于接收状态标记的中断处理和使用回调函数。通过示例代码详细阐述了每种方法的实现过程,包括中断服务函数、接收状态管理以及数据处理流程。同时,对比了两种方法的优缺点,指出回调函数在程序逻辑清晰度和执行效率上的优势。
摘要由CSDN通过智能技术生成

关于串口数据接收处理有多种方法,下面主要总结两种:

1.引入接收状态标记USART_RX_STA


int main(void)
{
    u8 len;
    u16 times = 0;

    HAL_Init();
    SystemClock_Config();	//初始化系统时钟为80M
    delay_init(80); 		//初始化延时函数    80M系统时钟
    uart_init(115200);		//初始化串口,波特率为115200

    LED_Init();				//初始化LED
    KEY_Init();				//初始化KEY

    while(1)
    {
        if(USART_RX_STA & 0x8000)
        {
            len = USART_RX_STA & 0x3fff; //得到此次接收到的数据长度
            //printf("\r\n发送的消息为:\r\n");
            HAL_UART_Transmit(&UART1_Handler, (uint8_t*)USART_RX_BUF, len, 1000);	//发送接收到的数据

            while(__HAL_UART_GET_FLAG(&UART1_Handler, UART_FLAG_TC) != SET);		//等待发送结束

            printf("\r\n\r\n");//插入换行
            USART_RX_STA = 0;
        }
        else
        {
            times++;

            if(times % 5000 == 0)
            {
                //printf("\r\nALIENTEK 潘多拉 STM32L475 IOT开发板 串口实验\r\n");
                //printf("正点原子@ALIENTEK\r\n\r\n\r\n");
				printf("%ld",times);
            }
            //if(times % 200 == 0)printf("请输入数据,以回车键结束\r\n");
            if(times % 30 == 0)LED_B_TogglePin; //闪烁LED,提示系统正在运行.

            delay_ms(10);
        }
    }
}
//定义变量USART_RX_STA,接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目

/**
 * @brief	串口1中断服务程序
 *
 * @remark	下面代码直接把中断控制逻辑写在中断服务函数内部
 * 			说明:采用HAL库处理逻辑,效率不高。
 *
 * @param   void
 *
 * @return  void
 */
void USART1_IRQHandler(void)
{
    u8 Res;

    if((__HAL_UART_GET_FLAG(&UART1_Handler, UART_FLAG_RXNE) != RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
        HAL_UART_Receive(&UART1_Handler, &Res, 1, 1000);

        if((USART_RX_STA & 0x8000) == 0) //接收未完成
        {
            if(USART_RX_STA & 0x4000) //接收到了0x0d
            {
                if(Res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始

                else USART_RX_STA |= 0x8000;	//接收完成了
            }
            else //还没收到0X0D
            {
                if(Res == 0x0d)USART_RX_STA |= 0x4000;
                else
                {
                    USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res ;
                    USART_RX_STA++;

                    if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收 USART_REC_LEN为定义的最大接收字节数
                }
            }
        }
    }
    HAL_UART_IRQHandler(&UART1_Handler);//处理串口中断请求
}

函数:HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint16_t *pData, uint16_t Size, uint32_t Timeout)
大致流程:判断是否忙–>锁住–>标记接收忙–>获取tick计数–>赋值RxXferCount有多少数据要接收–>每次从DR内获取一个Byte存在pData指向的空间
这个接收函数是阻塞式的。参数Timeout是超时时间,代表某次执行函数最多占用串口的时间,单位是毫秒。简单来说,在本次数据接收完之前,不能处理其他数据。所以,调用函数的时候要指明参数,本次接收占用多长时间,在此期间,串口资源被独占。如果在规定的时间内,数据接收完毕,那就释放占用的串口资源;如果到了时间,即便数据还没有接收完毕,(比如数据量很大),仍需要归还串口资源的控制权,供其他外设使用。

/**
  * @brief Receive an amount of data in blocking mode.
  * @note When FIFO mode is enabled, the RXFNE flag is set as long as the RXFIFO
  *       is not empty. Read operations from the RDR register are performed when
  *       RXFNE flag is set. From hardware perspective, RXFNE flag and
  *       RXNE are mapped on the same bit-field.
  * @param huart   UART handle.
  * @param pData   Pointer to data buffer.
  * @param Size    Amount of data to be received.
  * @param Timeout Timeout duration.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  uint16_t uhMask;
  uint32_t tickstart = 0;

  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return  HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->RxState = HAL_UART_STATE_BUSY_RX;

    /* Init tickstart for timeout managment*/
    tickstart = HAL_GetTick();

    huart->RxXferSize  = Size;
    huart->RxXferCount = Size;

    /* Computation of UART mask to apply to RDR register */
    UART_MASK_COMPUTATION(huart);
    uhMask = huart->Mask;

    /* as long as data have to be received */
    while (huart->RxXferCount > 0U)
    {
      if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
      {
        return HAL_TIMEOUT;
      }
      if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
      {
        tmp = (uint16_t *) pData ;
        *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
        pData += 2U;
      }
      else
      {
        *pData++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
      }
      huart->RxXferCount--;
    }

    /* At end of Rx process, restore huart->RxState to Ready */
    huart->RxState = HAL_UART_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

2.使用回调函数

串口中断的执行过程:

USART2_IRQHandler(void)      //启动文件中的中断向量,处理串口的总中断
HAL_UART_IRQHandler(UART_HandleTypeDef *huart)    //处理具体某个串口中断请求
UART_Receive_IT(UART_HandleTypeDef *huart)   //从数据寄存器读取一个字节数据存储在一个变量里,在接收完数据量为RxXferCount的数据后失能接收中断、赋值相应的标志位 接着执行回调函数
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

首先在启动文件中找到中断向量,然后依次go to definition 就可以顺藤摸瓜捋清串口中断的执行步骤。
接下来是回调函数的处理:

/**
  * 函数功能: 串口接收完成回调函数
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  HAL_UART_Transmit(&husartx,&aRxBuffer,3,0xffff);
  HAL_UART_Receive_IT(&husartx,&aRxBuffer,3);//再次使能串口接收中断
  //HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  //用户定义一个缓存区,大小随意,然后通过该函数把这个缓存区对应到串口的接收,
  //上面函数的意思就是把aRxBuffer作为缓存区,
  //然后usart1接收数据的时候就放到aRxBuffer这个地址及其递增的地址中
  //当接收到3Bytes数据之后调用一次callback函数,(可以设置接收固定长度数据后进入回调函数处理)
  //并使能串口接收中断功能。
}

显然使用回调函数程序逻辑清晰、代码简洁易懂,执行效率较高。
引用正点原子的流程图:
在这里插入图片描述

3.串口发送中断

串口发送中断使用较少,这里简单补充说明。
串口发送中断的过程大致如下:

USART2_IRQHandler(void)      //启动文件中的中断向量,处理串口的总中断
HAL_UART_IRQHandler(UART_HandleTypeDef *huart)    //处理具体某个串口中断请求
UART_EndTransmit_IT(UART_HandleTypeDef *huart)    //发送完成中断,失能发送完成中断,之后调用回调函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成后执行回调函数

使能中断发送:发送指定长度的数据并使能发送中断

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值