基于中断/DMA的串口通信

使用HAL库(或标准库)方式,设置USART1 波特率为115200,1位停止位,无校验位,分别采用中断方式、DMA方式完成下列任务:TM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送;

一、中断方式

1.1、设计思路

1.1.1、某些HAL库源码解析(可以不看)

使用中断方式,即采用HAL_UART_Receive_IT函数来实现。这是接收中断函数,输入一个字符即会触发一次中断,然后调用中断处理函数USART1_IRQHandler,该函数中默认执行HAL_UART_IRQHandler

if (errorflags == RESET)
  {
    /* UART in mode Receiver -------------------------------------------------*/
    if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      UART_Receive_IT(huart);
      return;
    }
  }

如果没有发生错误,则判断是否接收到数据(RXNE标志位被置位)并且接收中断使能(RXNEIE标志位被置位)。如果是则进入UART_Receive_IT(huart);这个函数的主要作用是从缓冲区读取数据,将数据存储到用户自定义的接收缓冲区中。

if (huart->RxState == HAL_UART_STATE_BUSY_RX)
  {
  if(huart->Init.WordLength == UART_WORDLENGTH_9B){
  //这里是数据长度为9,我用的是8,所以直接看后面...
  }else{
  //将数据存到8位临时变量中,将接收缓冲区指针递增一个字节
	  if (huart->Init.Parity == UART_PARITY_NONE)
	      {
	        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
	      }
      else
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      }
	  }
  }
//开始判断是否剩余需要读取的个数为0,即接收中断中定义的size以及全部接收完。如果全部读取完成则禁用UART的接收中断、奇偶校验错误中断,错误中断,并将接收状态设置为READY。然后调用接收完成回调函数;如果没有全接收完,则继续接收。
    if (--huart->RxXferCount == 0U)
    {
	      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
	      __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
	      __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
	      huart->RxState = HAL_UART_STATE_READY;
	
	#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
	      huart->RxCpltCallback(huart);
	#else
		  HAL_UART_RxCpltCallback(huart);
	#endif
	      return HAL_OK;
    }

1.1.2、程序执行流程设计

因为start和stop一个为5位,一个为4位,所以将接收中断函数的size设置为4,这样就可以在每读取完4个字节就进入接收完成回调函数,进行数据的处理。回调函数的大致流程:

  • 如果从串口1接收到的数据为stop则直接将RxFlag置为1,从而停止hello winows!的输出,并且重新启动接收中断。
if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"stop")==0){
		RxFlag = 1;
		HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
	}
  • 如果接受到的数据为star,则将flag标记为0,0表示还没有读取完(flag的作用是为了表示是否还需要多读取额外的一个字符,即总共读取5个字符),然后开启接收中断,接收一个字符
else if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"star")==0&&flag==1){
		flag = 0;
		HAL_UART_Receive_IT(huart, (uint8_t*)&ch, 1);
	}
  • 如果接受到的数据是t并且flag == 0,则总共接收到的五个字符是start所以就将RxFlag置为0,继续输出hello windows!,然后开启接收中断,4个字符
if(huart->Instance == USART1 && ch == 't' && flag == 0){
		RxFlag = 0;
		flag = 1;
		ch = '0';
		HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
	}

1.2、工程创建

可以参考上一篇博客https://blog.csdn.net/wakeup_high/article/details/134081617中的创建工程板块。

1.3、代码

这个实验都只在main.c文件中添加了代码

  • 添加头文件
#include "stdio.h"
#include "string.h"
  • 添加变量
uint8_t RxBuffer[LENGTH]; //字符缓冲区,LENGTH定义为4
uint8_t RxFlag = 0;//是否接受到stop
uint8_t ch;//额外字符't'的缓冲区
uint8_t flag = 1;//是否需要继续接收额外字符
  • 添加while循环
while (1)
  {
    /* USER CODE END WHILE */
	  if(RxFlag == 0){
		  HAL_UART_Transmit(&huart1,(uint8_t*)"hello windows!\r\n",16,0xffff);
		  HAL_Delay(1000);
	  }
    /* USER CODE BEGIN 3 */
  }
  • 添加中断接收回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
	if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"stop")==0){
		RxFlag = 1;
		HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
	}else if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"star")==0&&flag==1){
		flag = 0;
		HAL_UART_Receive_IT(huart, (uint8_t*)&ch, 1);
	}
	if(huart->Instance == USART1 && ch == 't' && flag == 0){
		RxFlag = 0;
		flag = 1;
		ch = '0';
		HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
	}
}

1.4、运行

在这里插入图片描述

1.5、波形图

在这里插入图片描述

二、DMA方式

2.1、执行流程

DMA是直接存储器访问,用于在外设和存储器之间以及存储器和存储器之间进行高速数据传输。DMA传输过程的初始化和启动由CPU完成,过程由DMA控制器来执行,节省了CPU参与。同时开启空闲中断,当串口助手发送完一个数据后,触发空闲中断,对数据进行判断是否为stop或者start然后修改RxFlag的状态,控制hello windows!的输出。

  • 开启空闲中断,DMA接收数据
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//开启空闲中断
  HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);//DMA接收数据使能
  • 空闲中断处理
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	HAL_UART_IRQHandler(&huart1);
  /* USER CODE END USART1_IRQn 0 */
  /* USER CODEBEGIN USART1_IRQn 1 */
	if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET){
		HAL_UART_DMAStop(&huart1);//停止DMA,避免覆盖RXBuffer
		__HAL_UART_CLEAR_FEFLAG(&huart1);
		if(strcmp((char *)RxBuffer, "stop")==0){
			RxFlag = 1;
		}else if(strcmp((char *)RxBuffer, "start")==0){
			RxFlag = 0;
		}
		//HAL_UART_Transmit_DMA(&huart1, (uint8_t*)RxBuffer, strlen((char *)RxBuffer));
		memset(RxBuffer,'\0',LENGTH);//因为start和stop长度不一样,重新覆盖不会完全覆盖,所以清0
		HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);
	}
  /* USER CODE END USART1_IRQn 1 */
}

2.2、工程创建

RCC修改晶振,时钟配置都与前面相同,只是多了DMA分配:
在这里插入图片描述

2.3、代码

  • main.h
#define LENGTH 100
extern uint8_t RxFlag;//是否接受到stop
extern uint8_t RxBuffer[LENGTH];//接收缓冲区
extern uint8_t RxCount;//接收数,等于LENGTH-已经接受到的
//因为这些变量在另一个c文件中调用了,所以要设置成外部变量,否则会因为一些莫名其妙覆盖
  • main.c
//初始化
uint8_t RxFlag=0;
uint8_t RxBuffer[LENGTH]={0};
uint8_t RxCount=0;
//main函数块中
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
  HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);
//while循环
while (1)
  {
    /* USER CODE END WHILE */
	  if(RxFlag == 0){
		  HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"hello windows!\r\n", 16);
		  HAL_Delay(1000);
	  }
    /* USER CODE BEGIN 3 */
  }
  • stm32f1xx_it.c中
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	HAL_UART_IRQHandler(&huart1);
  /* USER CODE END USART1_IRQn 0 */
  /* USER CODEBEGIN USART1_IRQn 1 */
	if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET){
		HAL_UART_DMAStop(&huart1);
		__HAL_UART_CLEAR_FEFLAG(&huart1);
		if(strcmp((char *)RxBuffer, "stop")==0){
			RxFlag = 1;
		}else if(strcmp((char *)RxBuffer, "start")==0){
			RxFlag = 0;
		}
		//HAL_UART_Transmit_DMA(&huart1, (uint8_t*)RxBuffer, strlen((char *)RxBuffer));
		memset(RxBuffer,'\0',LENGTH);//
		HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);
	}
  /* USER CODE END USART1_IRQn 1 */
}

2.4、运行结果

在这里插入图片描述

2.4、波形图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在使用STM32 HAL库进行串口通信时,可以使用中断接收DMA发送的方式来提高通信效率。 首先需要初始化串口,并配置接收中断DMA发送。以下是一个示例代码: ``` UART_HandleTypeDef huart1; void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* Enable the UART Parity Error Interrupt */ HAL_UART_Receive_IT(&huart1, rxBuffer, 1); /* Enable the DMA transfer for transmit */ HAL_UART_Transmit_DMA(&huart1, txBuffer, strlen((char *)txBuffer)); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { /* process received data */ HAL_UART_Receive_IT(&huart1, rxBuffer, 1); } } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { /* transmit completed */ } } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { /* handle UART error */ } } ``` 在上面的代码中,`USART1`是串口的实例,`rxBuffer`和`txBuffer`是接收和发送缓冲区。在串口初始化时,使用`HAL_UART_Receive_IT`函数开启接收中断,并使用`HAL_UART_Transmit_DMA`函数开启DMA发送。在接收中断回调函数`HAL_UART_RxCpltCallback`中,可以对接收到的数据进行处理,并继续接收下一个字节。在发送完成回调函数`HAL_UART_TxCpltCallback`中,可以进行一些操作,例如将发送缓冲区中的数据更新,等待下一次发送。在出现UART错误时,`HAL_UART_ErrorCallback`函数会被调用,可以在该函数中处理错误。 需要注意的是,在使用DMA发送时,需要保证发送缓冲区的数据不会被修改,直到DMA发送完成。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值