STM32 cubemx 开发系列文章(二)配置串口,完成回显小项目(DMA)

配置串口,完成串口回显

咱们书接上回,在cubemx配置好了串口工程所需要的所有选项之后,就可以正式进入MDK编写你的程序啦!

在开始本次的教程之前,我们首先需要知道stm32的串口都有什么样的工作模式,分别为查询模式,中断模式还有DMA模式,让我们一步步来。

查询模式

顾名思义,就是在程序的while(1)中不断的循环进行发送和接收的语句,这样的工作效率是非常底的,但是却可以让我们入门串口通信。
知道了查询模式的概念之后,我们就需要了解一下HAL库当中和串口发送接收有关的函数,并且熟悉他们的用法了。
和串口有关的底层驱动在stm32f1xx_hal_uart.c这个文件里,如图。在这里插入图片描述
这么多的函数看上去眼花缭乱的,但是在本章内容中,我们只需要了解接收和发送两个函数即可。

(1)串口发送函数
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
可以看到函数共有四个参数,分别为你在cubemx中初始化的第几号串口,要发送的数据,发送的数据长度,以及超时时间。具体用法为:
char str[12] = "Hello World\n";
HAL_UART_Transmit(&huart1, (uint8_t*)str, 12, 0xFFFF);
这样两句代码,就可以将Hello World从stm32发送到上位机。

(2)串口接收函数
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
此函数的四个参数和发送函数中的参数内容大体相同,只是将发送换为接收,具体的使用方法为:

	char recv_buf[13] = {0};
	
	  while (1)
	  {
	    /* USER CODE END WHILE */
	
	    /* USER CODE BEGIN 3 */
			//接收13个字节的数据,不超时
	    if(HAL_OK == HAL_UART_Receive(&huart1, (uint8_t*)recv_buf, 13, 0xFFFF))
	    {
	      //将接收到的数据发送
	      HAL_UART_Transmit(&huart1, (uint8_t*)recv_buf, 13, 0xFFFF);
	    }
	  }

效果如图所示:
在这里插入图片描述
到这里,我们已经了解了串口的查询模式。

中断模式

故名思意,在中断模式下,我们main函数无需每时每刻都在扫面串口接收的数据,只有当数据发送进来的时候采取进行数据的处理,这样也会极大的提高单片机的运行效率。
在开始中断模式之前,我们需要在cubemx设置中打开串口1的中断开关,如图:
在这里插入图片描述
然后我们点击生成工程,此时我们会发现在stm32f1xx_it.c文件里面多出来一个void USART1_IRQHandler(void)函数,没错,当串口中断被触发之后,就会自动进入这个函数中处理,因此我们只需要将逻辑处理函数在这个地方编写即可。

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
    uint8_t ch = 0;
    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) != RESET)
    {
        ch = (uint16_t)READ_REG(huart1.Instance -> DR);
        WRITE_REG(huart1.Instance -> DR,ch);
    }
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

由于种种原因,cubemx生成的初始化串口代码中并不包含使能串口的中断,因此如果直接这样写程序的话会发现程序并没有跑起来,其实不然,我们只需要在串口初始化的函数后面加上这样一句代码即可。
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
当串口接收中断被触发后,在中断处理函数中实现了将接收到的信息原封不动的发回给上位机,实现了串口的回显。

DMA模式

了解这个模式之前,我们首先要知道DMA是个什么东西,起到什么作用。
DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。
通俗的将就是DMA可以绕过cpu,将数据从一个寄存器直接搬到另一个寄存器,这样一来也会大大节约资源,加速程序的运行。

DMA模式串口回显

通过上述两种模式我们可以看出,虽然我们完成了串口回显的项目需要,但是我们每次只能传输指定长度的数据,这在以后的生产制造中显然是不现实的,我们每次要传输的数据一定是不定长的,这个时候就需要DMA前来救场。
那么DMA又是如何处理不定长数据的呢?
在DMA中有一个数据传输结束的中断标志,意思就是当cpu发起DMA传输之后,cpu便不再干涉传输的过程,但是身为开发者我们要如何知道数据是否传输完成呢,这时,当DMA传输完成后便会自动的向cpu发送一个中断,标志着数据传输完成,开发者便可以根据这个中断来开发实际的应用。
这里我们以串口接收DMA为例子讲解一下此过程。
首先,我们在配置工程中打开串口接收的DMA,如图:
在这里插入图片描述
生成工程之后,同样因为cubemx的bug,在串口初始化的时候,cubemx并不会自动帮我们使能,还需要我们手动添加代码。
首先找到usart.c,找到void MX_USART1_UART_Init(void),在这个函数的末尾处添加代码
HAL_UART_Receive_DMA(&huart1,Usart_Rx_Buf,RX_SIZE);Usart_Rx_Buf和RX_SIZE分别为自己定义的一个串口接收缓冲区的数组和该数组的大小,这部分可以根据需要自行定制,笔者暂时定义为512。
然后在这个c文件的末尾整合一下DMA回显的函数,便于调用。

void Uart_DMA_Rx_Data(UART_HandleTypeDef *huart)
{
	if(__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE))
		{
			__HAL_UART_CLEAR_IDLEFLAG(huart);
			HAL_UART_DMAStop(huart);
			HAL_UART_Receive_DMA(huart,Usart_Rx_Buf,RX_SIZE);
			printf("收到数据:%s\n",Usart_Rx_Buf);
		}
}

将这个函数加入到usart.h这个头文件中,再回到串口中断处理函数,将这个函数放入中断处理函数中,每次上位机向单片机发送数据后触发中断,在中断中DMA就会将Usart_Rx_Buf缓冲区中的数据发回上位机,实现串口回显项目的需求。

扫描二维码关注公众号,回复: 11191752 查看本文章
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	//函数传入的参数为你配置的串口号
	Uart_DMA_Rx_Data(&huart1);

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}
原创文章 3 获赞 2 访问量 245

猜你喜欢

转载自blog.csdn.net/qq_42092815/article/details/106119732