HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)

这篇具有很好参考价值的文章主要介绍了HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近想做一个控制电机的项目,其中会用到Pytho与单片机STM32之间的互同,最近也在看一些关于数据通信和拆包的相关知识,所以记录一下这段时间里对两者之间的互通所做的事情和发现的问题,以供自己和大家参考。

单片机的串口是我们常用的与电脑通信的外设,本次与Python互通就采用的串口实现上位机与下位机的通讯。

本章先讲解串口外设的使用,下一章讲解在Python中接收单片机发送的数据。

我采用的单片机型号是STM32F103ZET6,使用usart1进行数据的收发,所使用的引脚是PA9、PA10。使用STM32Cube打开串口进行初始化。

第一步,设置时钟源,在未设置的情况下,我们的单片机默认的系统时钟是8MHz,如下图所示。

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

所以,要想系统时钟达到最大就要使用外部晶振,不过,值得注意的是,F1系列的板子使用外部晶振作为时钟源时,系统时钟可超过72MHz,但是为了单片机的稳定性,我们对系统时钟的设置不能超过官方的限制。

下面,将打开外部时钟源

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

选择高速时钟源,然后选择72MHz,让系统自己设置。

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

第二步,在SYS里面的Debug选择 Serial write,非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

第三步打开串口USART1,在USART1中的Mode选择 Asynchronous 异步通信

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

波特率为 115200 Bits/s。传输数据长度为 8 Bit。奇偶检验 None,停止位 1 ,接收和发送都使能

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

 开启中断

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

并且采用DMA进行数据传输,采用DMA 好处是不需要占用CPU的资源即可完成数据的接收和发送,极大的节约了CPU 的占用。对于DMA 的原理这里不重点解释,我们只需要知道他的功能和如何使用即可,下面将USART1的DMA开启。

选择DMA Settings点击Add,添加USART1_RXUSART1_TX

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

选择MDK-ARMCode Generator 中的 Generate peripheral initialization as a pair of '.c/.h' files per peripheral

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

 最后生成文件

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

生成的代码如下,这里说一个小技巧,我们可以在

/* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

中间写函数,例如

/* USER CODE BEGIN 1 */

      interesting();

  /* USER CODE END 1 */

这样,如果向开其他外设的时候直接在Cube里面打开,再生成即可,如果不卸载里面的话,我们自己写的代码将会在生成后被删除,会很麻烦。 

main.c 

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 */

  /* USER CODE END 2 */

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

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

usart.c

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;

/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  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();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA1_Channel5;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Channel4;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);
    HAL_DMA_DeInit(uartHandle->hdmatx);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

串口使能中断函数

void usart_dma_int()
{
	
	__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断

	HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//开启DMA接收
}

DMA发送函数

HAL_UART_Transmit_DMA(&huart1, buf,len)

重定向printf函数

int fputc(int ch,FILE *stream)
{
HAL_UART_Transmit(&huart1,( uint8_t *)&ch,1,0xFFFF);
return ch;
}

我们的接收数据采用DMA接收方式,使用DMA+IDLE空闲中断,这样的好处是,当接收到一串数据时,串口不会发生中断,而是将接收到的数据通过DMA存到缓存区中,当数据传输完成后,IDLE产生中断标志位,进而产生一次中断,我们就可以在这次中断中做一些我们想要实现的功能,这种DMA+IDLE接收的方式,很符合数据传输通讯的形式,代码如下

void USART1_IRQHandler(void)
{
        uint32_t flag = 0;
	    uint32_t num;//DMA没有传输的个数
	    flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
	if((tmp_flag != RESET))//IDE产生中断
	{ 
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
		HAL_UART_DMAStop(&huart1); //停止DMA传输
		num = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数   
		rx_num =  BUFFER_SIZE - temp; //BUFFER_SIZE(接收数据缓存的最大个数)-num(剩余的个数)=当前接收的数据个数
		flag= 1;	// 接受完成标志位置1	
	}
  /* USER CODE END USART1_IRQn 0 */
   HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

主函数

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 */
  usart_dma_int();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
     printf("实验\r\n");
     HAL_Delay(300);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

实验结果

HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码),单片机,stm32,嵌入式硬件,python,c语言

 本次先讲解串口收发的使用,下一章介绍在Python中接收单片机发送的数据并解析。

跳转连接:https://blog.csdn.net/m0_73816319/article/details/135667240?spm=1001.2014.3001.5502文章来源地址https://www.toymoban.com/news/detail-814070.html

到了这里,关于HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • GD32实现串口空闲(IDLE)中断 + DMA机制接收数据

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

    2023年04月13日
    浏览(37)
  • STM32—HAL库中断/DMA控制和完成串口通信

    目录 一、解决的问题 二、串口通讯协议和RS-232的介绍以及USB/TTL转232模块的工作原理   1、 串口协议和RS-232标准:  (1)串口协议: (2)RS-232 标准:   2、RS232电平与TTL电平的区别   3、USB/TTL转232“模块(CH340芯片为例)  (1)基本原理:  (2)CH340模块介绍: ​三、搭

    2024年02月02日
    浏览(61)
  • STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

    能够点进这篇文章的小伙伴肯定是对STM32串口DMA空闲中断接收数据感兴趣的啦,今天用这一功能实现串口解析航模遥控器sbus信号时,查阅了很多网友发布的文章(勤劳的搬运工~),包括自己之前写过一篇博客 STM32_HAL库_CubeMx串口DMA通信(DMA发送+DMA空闲接收不定长数据)。本文

    2024年02月09日
    浏览(55)
  • (stm32之HAL库)UART工作在DMA模式要打开串口中断吗?

    最近学习了stm32(F4xx)的串口在DMA模式下的使用,期间以ST官方提供的例程进行参考学习,发现其初始化过程中是打开了UART的中断的,而且HAL库中stm32f4xx_hal_uart.c文件中的DMA模式使用说明里也有这么一句话: 即在非循环模式下(也就是发完一次数据就停止的常用模式)需要配置

    2024年02月12日
    浏览(29)
  • 【STM32】CUBEMX之串口:串口三种模式(轮询模式、中断模式、DMA模式)的配置与使用示例 + 串口重定向 + 使用HAL扩展函数实现不定长数据接收

    目录   总览 使用CUBEMX创建工程的基本配置 CUBEMX中的配置 Keil中的配置 实物连接 串口轮询模式 轮询模式HAL库函数 特点 实验一:发送数据给单片机并让其返回相同值 串口重定向 串口中断模式 在CUBEMX中打开串口中断 中断模式HAL库函数 特点 实验二:使用中断回调完成实验一

    2024年04月10日
    浏览(44)
  • STM32F4_HAL库_串口阻塞/中断/DMA三种方式发送数据的配置

    串口阻塞发送的意思就是,发送一段数据,在没有发送完所有数据之前,一直停留在此发送函数(可设定阻塞时间),这个过程中会阻塞别的程序运行; HAL库的配置分为两个层次,一个是HAL库内部调用的、与MCU硬件相关的初始化xxx_MspInit,一个是我们外部调用的初始化xxx_In

    2023年04月25日
    浏览(43)
  • GD32或STM32:DMA循环模式与普通模式(串口IDLE)

    USART+DMA+循环队列接收不定长数据-CSDN博客 STM32 DMA 循环模式DMA_Mode_Circular详解-CSDN博客 推荐以上两个链接。 Normal(普通)模式的DMA+串口IDLE中断,流程如下: 1、初始化时:开启串口IDLE中断;dma_circulation_disable,失能DMA的循环; 2、在串口IDLE中断里面: (1) 清除idle的flag;dma_c

    2024年02月21日
    浏览(34)
  • STM32 F4串口空闲中断 + DMA实现数据发送

    最近在做 STM32 + ROS车的项目,STM32与ROS之间通信由于数据量大,所以在 STM32端 使用 空闲中断 + DMA 的方案来减轻 CPU 的压力。 一、空闲中断 空闲中断 顾名思义为空了,闲了,没事了进的中断,在 没有数据流 的时候会进入进行读取。 在我们串口进行发送时实则为连续发送,两

    2024年02月16日
    浏览(53)
  • STM32 UART串口通信IDLE空闲中断的使用步骤

    参考了各路大神的资料,蒙蔽了半天,终于学会了,记录一下,以后忘了可以回来复习参考。 一、首先在stm32cube中配置打开对应uart串口的中断 二、工程main函数调用 __HAL_UART_ENABLE_IT(huart1,UART_IT_IDLE);//hal库宏定义,使能串口空闲中断     HAL_UART_Receive_DMA(huart1,data,sizeof(data));//使

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

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

    2024年02月03日
    浏览(35)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包