使用STM32串口+空闲中断+DMA接收不定长数据

一,为什么使用空闲中断

        之前做串口数据处理的时候,总是遇到一个问题,就是串口在接收大量数据的时候,我们总是要等所有数据发送完成才去处理(一边接收一边处理数据是不可靠的),那么这里便存在一个问题,“接收数据多大呢,什么时候才接收完成?”。

因此,针对这类不定长的数据接收,我们使用串口空闲中断,就是在接收一个字节的时间内,如果没有在这段时间内没有再接收到输出,单片机串口则触发空闲中断。

使用串口空闲中断之后,我们也不需要总是触发串口接收中断去搬运数据,这样可以提高系统的运行速度和稳定性。

在编程之前,我们需要了解一些关于串口中断触发的概念。

二,串口空闲中断介绍

下面是关于串口空闲中断的寄存器位

空闲中断触发原理:IDLE中断使能之后,当RXNE数据寄存器接收到数据,同时IDLE检测总线空闲置1之后(简单说就是串口停止接收数据之后),此时会触发串口空闲中断,用户可通过标志位去处理空闲中断串口接收的全部数据。

三,STM32CubeMx配置

下面是HAL库关于串口的一些配置,由于串口的时钟源是固定的,所以我们不需要手动去配置时钟,我们直接进入串口模块进行配置即可。

首先,选择串口的工作模式,为异步串口通信。

参数一般默认即可。

然后启动发送与接收中断。

接下来,我们我们配置串口的DMA接收与发送,设置优先级,设置传输数据大小为一字节

点开NVIC,选择中断优先级为3

接下来生成工程,进入用户配置即可。

(如果对DMA不了解的小伙伴,可以先去学习一下,了解DMA的原理即可)

四,编程实战

1,修改stm32f1xx_it.c

修改公共中断处理函数,添加用户自定义的空闲中断回调函数(HAL库没有提供相应的回调函数)

(大家记得引用usart.h头文件)


/**

  * @brief This function handles USART1 global interrupt.

  */

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
  if(USART1 == huart1.Instance)     // 判断是否是空闲中断                                
  {     
    if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))  
    {         
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);  // 清除空闲中断标志)                 
        printf("\r\nUART1 Idle IQR Detected\r\n");

        USAR_UART_IDLECallback(&huart1); //调用用户空闲中断回调函数                       
    }

  }
  /* USER CODE END USART1_IRQn 0 */

  HAL_UART_IRQHandler(&huart1);

  /* USER CODE BEGIN USART1_IRQn 1 */
  /* USER CODE END USART1_IRQn 1 */
}

2,修改usart.c

在生成的串口c文件中,重定义printf,同时定义我们用户串口空闲中断回调函数


/* USER CODE BEGIN 1 */

uint8_t receive_buff[255]; // DMA接收缓冲数组  

/*
  重定义printf
*/
int fputc(int ch, FILE *f)
{
    while ((USART1->SR & 0X40) == 0);    
    USART1->DR = (uint8_t)ch;      
    return ch;
}


/*
  重定义串口接收回调函数
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

}


/*
  用户自定义串口空闲中断回调函数
*/                                                
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart)
{
    HAL_UART_DMAStop(&huart1); // 停止本次DMA传输 
   
    // 计算接收到的数据长度
    uint8_t data_length  = 255 - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
	
    printf("Receive Data(length = %d): ",data_length);// 测试函数:将接收到的数据打印出去
    HAL_UART_Transmit(&huart1,receive_buff,data_length,0x200);                     
    printf("\r\n");
	
    memset(receive_buff,0,data_length); // 清零接收缓冲区                                           
    data_length = 0;
    
    // 重启开始DMA传输 每次255字节数据                  
    HAL_UART_Receive_DMA(&huart1, (uint8_t*)receive_buff, 255);
}



/* USER CODE END 1 *

3,修改usart.h

对外调用接收缓冲数组,以及用户串口空闲中断回调函数。

extern uint8_t receive_buff[255]; 
  
/* USER CODE END Includes */

extern UART_HandleTypeDef huart1;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_USART1_UART_Init(void);

/* USER CODE BEGIN Prototypes */
void USAR_UART_IDLECallback(UART_HandleTypeDef *huart);

4,修改main.c

在主函数里面,串口正常输出与DMA输出测试,开启串口空闲中断,开始串口DMA接收。


/* USER CODE BEGIN 0 */
uint8_t Senbuff[] = "Q大帅のUART DMA Test Success ! \r\n";   
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  //测试普通发送及重定义printf
  printf("hell world!\r\n");
  //测试DMA发送
  HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
  //使能串口空闲中断 
  __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); 
  //使用DMA接收串口数据
  HAL_UART_Receive_DMA(&huart1, (uint8_t*)receive_buff, 255);  
    
    
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_Delay(10);
    
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
   

值得一提的是,单独使用串口空闲中断,不使用DMA接收也可以实现这个功能,只是速度会慢一点,本质上我们只需要添加一个串口数据接收完成的标志位即可,这部分大家有空摸索一下吧。(个人理解,实际上可能有问题)

至此,我们就完美解决对于不定长数据的接收啦,完结,撒花!!!

  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值