STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据

这篇具有很好参考价值的文章主要介绍了STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.串口轮询模式底层机制:

       在STM32每个串口的内部都有两个寄存器:发送数据寄存器(TDR)/发送移位寄存器,当我们调用HAL_UART_Transmit 把数据发送出去时,CPU会将数据依次将数据发送到数据寄存器中,移位寄存器中的数据会根据我们设置的比特率传化成高低电平从TX引脚输出。待发送移位寄存器中发数据发送出去后,CPU就会将下一个数据进行相同的发送。

        当我们调用HAL_UART_Receive把数据接收过来时,数据会通过RX引脚收到的电平信号进行转化后,会将数据存进接收移位寄存器。接收移位寄存器每接收完1帧就会将数据放到接收数据寄存器。而后CPU会将接收数据寄存器中的数据存到变量中

STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据,嵌入式开发,stm32,单片机,DMA,中断,串口   STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据,嵌入式开发,stm32,单片机,DMA,中断,串口

        而在轮询模式下。在发送整个数据的过程中,CPU都要不断地轮询“发送数据寄存器”中的数据是否移动到“发送移位寄存器”下,直到把本次要发送的数据全部发完,或者用时超过设置的超时时间才算结束。

        因此,采用轮询模式,在数据接收和发送过程中,CPU不会去做其他事情,主程序中的代码会进行阻塞直到IO结束。

具体的案例在下面链接:

STM32:TTL串口调试-CSDN博客

二.串口的中断模式

(1).中断模式机制

        采用中断模式便可以解决在IO过程中主程序阻塞问题。原理是接收和发送数据时,CPU并不会轮询发送/接收数据寄存器是否有数据。而是发送/接收数据寄存器当每数据时会发送一个中断主动通知CPU。因此CPU在将数据寄存器中的数据移动到移位寄存器后,就可以去执行其他任务了。当发送移位寄存器中的数据发送出去后就会触发“发送移位寄存器空”中断再把CPU叫回来。如此反复完成IO。

(2).中断模式案例

在STM32:TTL串口调试-CSDN博客这个案例下,改造成中断函数形式。

 1. 打开CubeIDE,开启USART2中断,生成代码

STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据,嵌入式开发,stm32,单片机,DMA,中断,串口

2.查看stm32f1xx_it文件中 USART2_IRQHandler() 中断处理函数的定义。由于每个USART中只有一个中断向量,并且这个中断向量是USART中断请求共用的,所以中断处理函数也是被USART共用的。因此,为了单独写发送数据的逻辑写在中断处理函数中就不太合适。因此需要判断哪些原因触发了这个中断处理函数,分别实现逻辑,而这个判断HAL_UART_IRQHandler(&huart2) 函数中已经帮我们准备好了。

STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据,嵌入式开发,stm32,单片机,DMA,中断,串口

        转到HAL_UART_IRQHandler(&huart2)的定义。可以看见经过一系列判断等逻辑后就会根据判断的结果执行Callback函数。因此当某个事件发生时,就会调用回调函数。而数据接收完成后执行的回调函数就是:

__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

注:Cplt 指 完成 。 __weak 关键字作为前缀代表是一个弱定义,我们可以在其他地方重新定义此函数

因此我们可以实现这个回调函数来实现传输数据又不阻塞主程序。

(3).示例代码

main.c关键代码如下:

注:  HAL_UART_Receive_IT(&huart2, &message, size);
       HAL_UART_Transmit_IT(&huart2,&message, size);

是中断形式的UART接收/发送数据的函数,由于不阻塞主程序因此不需要设置超时时间。

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
uint8_t  recvDate[2];

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
	//	  HAL_UART_Receive(huart, pData, Size, Timeout)
//		  HAL_UART_Receive(&huart2, recvDate, 2, HAL_MAX_DELAY);
		  HAL_UART_Transmit_IT(&huart2,recvDate,2);
		  GPIO_PinState pinstate= GPIO_PIN_RESET;
		  if(recvDate[1] == '1'){
			  pinstate = GPIO_PIN_SET;
		  }
		  if(recvDate[0] == 'R'){
			  HAL_GPIO_WritePin(redLED_GPIO_Port, redLED_Pin, pinstate);
		  }else if(recvDate[0] == 'B'){
			  HAL_GPIO_WritePin(blueLED_GPIO_Port,blueLED_Pin, pinstate);
		  }else if(recvDate[0] == 'G'){
			  HAL_GPIO_WritePin(greenLED_GPIO_Port,greenLED_Pin, pinstate);
		  }
		  HAL_UART_Receive_IT(&huart2, recvDate, 2);

}
/* 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_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2, recvDate, 2);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    /* USER CODE END WHILE */

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

三.DMA 模式

       虽然采用中断模式便可以解决在IO过程中主程序阻塞问题,但是CPU切换过于频繁。而直接内存访问(DMA,Direct Memory Access)是一些计算机总线架构提供的功能,它能使数据从附加设备(如磁盘驱动器)直接发送到计算机主板的内存上。

        CPU和寄存器就像老师与学生。轮询模式就像老师每讲完一段知识点,老师都会不断地问学生学好了没,直到学会才会讲下一个知识点。中断模式就像老师每讲完一段知识点后就开始干自己的事,等待学生举手示意自己学习完后才开始讲下一个知识点。而DMA就像一名助教,负责提前学习老师要讲给学生的知识,助教再将所学知识讲给学生。直到学生把助教所学的知识都学完后,助教再让教师再传授一部分知识。

        再CubeIDE设计界面中,connective ->USART2-> DMA Settings 可以配置DMA通道(如下图).

想要发送数据(TX),即内存向外设传输数据,默认通道为Channal7,而接收为Channal6。目前采用默认配置就行。

STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据,嵌入式开发,stm32,单片机,DMA,中断,串口

配置完后,传输和发送数据的函数就变成了

注:  HAL_UART_Receive_DMA(&huart2, &message, size);
       HAL_UART_Transmit_DMA(&huart2,&message, size);

 当然,还可以利用中断来通知CPU传输/发送数据,只不过就不是原先的串口中断,而是DMA传输完成中断了。

STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据,嵌入式开发,stm32,单片机,DMA,中断,串口

四.接收不定长数据

(1) 接收不定长数据的原理

        接收不定长数据主要关心的是"串口空闲(Idle)中断",即接收串口(RX引脚)上无后续数据进入便会触发。通常这个场景代表一帧数据包接收完成

        而数据接收关键函数就变成了:

      HAL_UARTEx_ReceiveToIdle(&huart2, pData, Size, RxLen, Timeout)

      //size为允许装入的最大数据长度。

      HAL_UARTEx_ReceiveToIdle_IT(&huart2, &message,maxsize);

      HAL_UARTEx_ReceiveToIdle_DMA(&huart2, &message, maxsize);

        回调函数就变成了

// Size参数传入数值为本次接收的数据长度

__weak void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)

对于该回调函数,除了"串口空闲中断"会调用以外,DMA传输过半中断也会调用。因此需要根据业务要求决定无关中断是否要屏蔽。文章来源地址https://www.toymoban.com/news/detail-726562.html

(2).采用DMA方式接收不定长数据的示例代码

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */
uint8_t  recvDate[20];
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart2;
DMA_HandleTypeDef hdma_usart2_tx;
DMA_HandleTypeDef hdma_usart2_rx;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size){
	if(huart == &huart2){
		 //把接收到的数据,发给终端进行打印
		 HAL_UART_Transmit_DMA(&huart2,recvDate,Size);
		 GPIO_PinState pinstate= GPIO_PIN_RESET;
		 if(recvDate[1] == '1'){
			 pinstate = GPIO_PIN_SET;
		 }
		 if(recvDate[0] == 'R'){
			 HAL_GPIO_WritePin(redLED_GPIO_Port, redLED_Pin, pinstate);
		 }else if(recvDate[0] == 'B'){
			 HAL_GPIO_WritePin(blueLED_GPIO_Port,blueLED_Pin, pinstate);
		 }else if(recvDate[0] == 'G'){
			 HAL_GPIO_WritePin(greenLED_GPIO_Port,greenLED_Pin, pinstate);
		 }
		 //继续接收即将要接收的数据
		  HAL_UARTEx_ReceiveToIdle_DMA(&huart2, recvDate, sizeof(recvDate));
		  //关闭DMA传输过半中断
		  __HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
	}
}
/* 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_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  //接收数据,并屏蔽DMA传输过半中断
  HAL_UARTEx_ReceiveToIdle_DMA(&huart2, recvDate, sizeof(recvDate));
  __HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    /* USER CODE END WHILE */

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

到了这里,关于STM32:串口轮询模式、中断模式、DMA模式和接收不定长数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GD32F4单片机实现接收超时中断+DMA实现串口的不定长接收和DMA发送

    环形缓冲区+定时器超时中断的方式 优点 环形缓冲区可以接收多帧数据 数据帧超时间隔可以设置 缺点 设备任务比较繁重时,使用中断接收可能会丢失数据。尤其是在长时间关闭中断或者串口中断优先级不高时 频繁进出中断。在使用RTOS的系统中,每收到一个数据就会进行一

    2024年02月15日
    浏览(63)
  • stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行

    空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。 空闲中断的优点在于省去了帧头帧尾的检测 ,进入中断程序即意味着已经接收到一组完整数据,仅需

    2024年02月03日
    浏览(60)
  • 衔尾法解决当无法使用空闲中断以及DMA中断时配置DMA接收串口不定长数据

    问题:类似K线与蓝牙接收模块,要求由原来的接收串口中断改为DMA接收。据说要用到空闲中断与DMA中断,但是经仿真发现DMA每完成传输一个数据(比如1BYTE)就会进入空闲中断(k线发现这种情况),考虑到这样进入中断的频率和以前串口接收中断的频率差不多,所以放弃此方案,

    2024年02月09日
    浏览(56)
  • STM32使用三种方式(阻塞、中断、DMA)实现串口发送和接收数据

    记录下学习STM32开发板的心得的和遇见的问题。 板卡型号:STM32F405RGT6 软件:STM32CubeMX、IAR STM32串口外设提供了3种接收和发送方式:阻塞、中断、DMA,主要给大家分享中断方式接收不定长数据和DMA使用空闲中断接收不定长数据。 阻塞发送: 阻塞接收: 两个函数需要注意的就

    2024年02月03日
    浏览(52)
  • STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

    方法一、使用宏定义判断IDLE标志位 空闲的定义是总线上在一个字节的时间内没有再接收到数据,USART_IT_IDLE空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。 串口空闲中断(UART_IT_IDLE):STM32的IDLE的中断在串口无数据接收的情况

    2024年01月23日
    浏览(58)
  • STM32学习笔记(五)串口空闲中断+DMA实现不定长收发(stm32c8t6)

    记录一下学习过程 DMA DMA,全称为: Direct Memory Access,即直接存储器访问, DMA 传输将数据从一个 地址空间复制到另外一个地址空间。 这一过程无需cpu的参与,从而提高cpu使用的效率 DMA相关的参数:1 数据的源地址、2 数据传输的目标地址 、3 传输宽度,4 传输多少字节,5 传

    2024年02月14日
    浏览(47)
  • GD32实现串口空闲(IDLE)中断 + DMA机制接收数据

    前言 串口功能在单片机开发中,是比较常用的外设,熟练使用串口功能也是驱动开发必备的技能之一。 DMA 是一种CPU辅助手段,可以在CPU不参与的情况下,是做一些辅助CPU的事情,如通常的数据搬运。 在没有DMA之前,数据读取时,需要CPU的处理,在多任务处理时,增加资源紧

    2023年04月13日
    浏览(48)
  • STM32 串口DMA接收数据(高效接收数据)

    极度不推荐在使用DMA的时候按照传统的方式进行重定义!!! 非常简单,轮询方式整个CPU 在串口发送时处于等待状态,但是使用DMA时无法确保当前DMA已经传输完成。 有同学可能会认为可以通过判断DMA的传输标志位来进行等待,但如果这样的话就丧失了DMA的设计意图: 再次使

    2024年02月16日
    浏览(52)
  • STM32实现DMA接收串口数据

    一..首先我们得配置DMA和USARAT,我们的原理是DMA1的通道5为USART1的RX引脚。  1.USART1的配置 2.DMA的配置 二.中断进行数据处理(stm32f10x_it.c) 我们可以串口打印出数组中的数据,验证DMA是否正常工作。可以到数据处理那个地方进行处理。USART1在初始化中就已经波特率为115200.我们可以

    2024年02月16日
    浏览(47)
  • STM32 串口 DMA 接收任意长度数据

    DMA 传输完成会产生中断告知 CPU,这对于固定长度的数据是没什么问题的。但是对于不定长的数据就不行了,DMA 一定要接收到足够多(设定的长度)的数据时才产生完成中断,如果接收到的数据量小于设定的长度,这个时候 CPU 就无法通过中断方式取处理这点数据了。那 CPU

    2024年02月11日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包