STM32基础知识:串口通信-DMA方式

发布者:真诚的友谊最新更新时间:2024-04-22 来源: elecfans关键字:STM32  串口通信  DMA方式 手机看文章 扫描二维码
随时随地手机看文章

1 DMA概述

直接存储器访问 (DMA) : 用于在外设与存储器之间以及存储器与存储器之间进行高速数据传输。DMA传输过程的初始化和启动由CPU完成,传输过程由DMA控制器来执行,无需CPU参与,从而节省CPU资源,提高利用率。

DMA数据传输的四个要素:

  • 传输源 :DMA数据传输的来源

  • 传输目标:DMA数据传输的目的

  • 传输数量:DMA传输数据的数量

  • 触发信号:启动一次DMA数据传输的动作

STM32的DMA控制器特点

图片

  1. 每个DMA控制器有8个数据流,每个数据流可以映射到8个通道(或请求);

  2. 每一个DMA控制器用于管理一个或多个外设的存储器访问请求,并通过总线仲裁器来协调各个DMA请求的优先级;

  3. 数据流(stream)是用于连接传输源和传输目标的数据通路,每个数据流可以配置为不同的传输源和传输目标,这些传输源和传输目标称为通道(Channel);

  4. 具备16字节的FIFO。使能FIFO功能后,源数据先送入FIFO,达到FIFO的触发阈值后,再传送到目标地址。
    图片

DMA数据传输方式

  • 普通模式:传输结束后(即要传输数据的数量达到零),将不再产生DMA操作。若 开始新的DMA传输,需在关闭DMA通道情况下,重新启动DMA传输。

  • 循环模式:可用于处理环形缓冲区和连续数据流(例如ADC扫描模式)。当激活循 环模式后,每轮传输结束时,要传输的数据数量将自动用设置的初始值进行加载,并继续响应DMA请求。

2 DMA方式的接口函数

  1. 串口DMA方式发送函数:HAL_UART_Transmit_DMA

    函数原型HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size)
    功能描述在DMA方式下发送一定数量的数据
    入口参数1huart:串口句柄的地址
    入口参数pData:待发送数据的首地址
    入口参数3Size:待发送数据的个数
    返回值HAL状态值:HAL_OK表示发送成功;HAL_ERROR表示参数错误;HAL_BUSY表示串口被占用;
    注意事项1. 该函数将启动DMA方式的串口数据发送2. 完成指定数量的数据发送后,可以触发DMA中断,在中断中将调用发送中断回调函数HAL_UART_TxCpltCallback进行后续处理3. 该函数由用户调用户调用
  2. 串口DMA方式接收函数:HAL_UART_Receive_DMA

    函数原型HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_Handle TypeDef *huart, uint 8_t *pData, uint 16_t Size)
    功能描述在DMA方式下接收一定数量的数据
    入口参数1huart:串口句柄的地址
    入口参数pData:待接收数据的首地址
    入口参数3Size:待接收数据的个数
    返回值HAL状态值:HAL_OK表示发送成功;HAL_ERROR表示参数错误;HAL_BUSY表示串口被占用;
    注意事项1. 该函数将启动DMA方式的串口数据接收2. 完成指定数量的数据接收后,可以触发DMA中断,在中断中将调用接收中断回调函数HAL_UART_ExCpltCallback进行后续处理3. 该函数由用户调用户调用
  3. 获取未传输数据个数函数:__HAL_DMA_GET_COUNTER

    函数原型__HAL_DMA_GET_COUNTER
    功能描述获取DMA数据流中未传输数据的个数
    参数HANDLE :串口句柄的地址
    返回值NDTR寄存器的内容,即DMA数据流中无传输数据的个数
    注意事项1. 该函数是宏函数,进行宏替换,不发生函数调用2. 该函数需要由用户调用,用于获取未传输数据的个数
  4. 关闭DMA数据流:__HAL_DMA_DISABLE

    函数原型__HAL_DMA_DISABLE(__HANDLE__)
    功能描述关闭指定的DMA数据流
    参数HANDLE :串口句柄的地址
    返回值
    注意事项1. 该函数是宏函数,进行宏替换,不发生函数调用2. 该函数需要由用户调用,用于关闭指定的DMA数据流3. 关闭DMA数据流后触发DMA中断,最终调用串口收发的回调函数

任务实践4

不定长数据的收发:利用串口调试助手,从PC上发送任意长度的字符到开发板,开发板收到后原样发回到PC。

空闲中断的特点:

  1. 在一帧数据传输结束后,通信线路将会维持高电平,这个状态称为空闲状态;

  2. 当CPU检测到通信线路处于空闲状态时,且空闲状态持续时间大于一个字节传输时间时,空闲状态标志IDLE将由硬件置1。如果串口控制寄存器CR1中的IDLEIE位为1,将会触发空闲中断( IDLE中断);

  3. 由于空闲标志是在一帧数据传输完成后才置位,在有效数据传输过程中不会置位,因此借助空闲中断,可以实现不定长数据的收发。

设计思路:

  1. 使能IDLE中断,在串口2的中断服务程序USART2_IRQHandler中添加对IDLE中断的判断,该函数位于stm32f4xx_it.c文件;

  2. 设置传输模式为普通模式,启动DMA传输。串口一旦接收到数据,则触发DMA操作,将数据存放到用户定义的接收缓冲区;

  3. 当一帧数据发送完成后,线路处于IDLE状态,将触发IDLE中断,调用IDLE中断回调函数,设置数据接收完成标志;

  4. 主程序检测到接收完成标志置位后,将接收的一帧数据原样发回到PC,并禁能DMA,以触发DMA中断。DMA中断将调用接收中断回调函数,在回调函数中重新启动DMA传输。

  5. 串口1的DMA配置
    图片
    DMA数据流的中断使能由CubeMX自动勾选,手动使能串口2中断
    图片

编写程序

在stm32f1xx_it.c中添加空闲中断的处理


/**

  * @brief This function handles USART1 global interrupt.

  */

void USART1_IRQHandler(void)

{

  /* USER CODE BEGIN USART1_IRQn 0 */


  /* USER CODE END USART1_IRQn 0 */

  HAL_UART_IRQHandler(&huart1);

  /* USER CODE BEGIN USART1_IRQn 1 */


  // Add handling of idle interrupts

  if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE != RESET))

  {

    __HAL_UART_CLEAR_IDLEFLAG(&huart1);   // Clear the IDLE interrupt flag

    HAL_UART_IdleCpltCallback(&huart1);   // User-written IDLE interrupt callback function

  }

  /* USER CODE END USART1_IRQn 1 */

}

在main.c中


添加用户宏变量、变量定义


/* USER CODE BEGIN PM */

#define LENGTH 100   // Receive buffer size

/* USER CODE END PM */


/* USER CODE BEGIN PV */

uint8_t RxBuffer[LENGTH];

uint8_t RecCount = 0;

uint8_t RxFlag = 0;

/* USER CODE END PV */

声明和定义空闲中断回调函数,定义DMA接收中断回调函数


/* USER CODE BEGIN PFP */

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)

/* USER CODE END PFP */


/* USER CODE BEGIN 4 */

int fputc (int ch, FILE *f)

{

    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);

    return ch;

}


int fgetc(FILE *f)

{

    uint8_t ch = 0;

    HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);

    return ch;

}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

  if (huart- >Instance == USART1)

  {

    HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);

  }

}


void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)

{

  RxFlag = 1;

}

/* USER CODE END 4 */

编写用户应用代码


复制

/* USER CODE BEGIN 2 */

  printf('***  UART coummunication using IDLE IT + DMAn');

  pringf('PLease enter arbitrary length characters:n');

  __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);

  HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);

 /* USER CODE END 2 */


  /* USER CODE BEGIN 3 */

    if (RxFlag == 1)

    {

      RxFlag = 0;

      RecCount = LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

      HAL_UART_Transmit_DMA(&huart1, (uint8_t*)RxBuffer, RecCount);

      RecCount = 0;

      __HAL_DMA_DISABLE(&hdma_uasrt1_rx);

    }

  }

  /* USER CODE END 3 */


关键字:STM32  串口通信  DMA方式 引用地址:STM32基础知识:串口通信-DMA方式

上一篇:基于FreeRTOS的STM32F103系统—队列
下一篇:MathWorks专访:如何用STM32设计出超越AI的智能应用

推荐阅读最新更新时间:2026-03-20 01:00

STM32的串口采用DMA方式发送数据测试
环境: 主机:WIN7 开发环境:MDK4.23 MCU:STM32F103CBT6 源代码: 配置: //---------------------串口功能配置--------------------- //打开串口对应的外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE); //启动DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //DMA发送中断设置 NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
[单片机]
STM32基础设计(6)---ADC转换(DMA方式
本文简单介绍了STM32F103C8,通过DMA方式读取ADC并通过串口中断向电脑端打印出当前电源ADC的值。 现在先将设计过程的主要步骤介绍如下: 1,串口配置 2,中断配置 3,DMA配置 4,ADC配置 5,中断服务函数 6,主函数 先总结下博主在这次基础设计中犯的错误,在中断初始化函数中,没有将中断通道使能,导致电脑端没有接收到数据,发现后就去检查串口初始化函数了,结果没有发现错误,而是检查了一遍代码才发现错误。发现串口无法工作后,先核查初始化函数,如果问题没有解决,第二步,如果是串口中断方
[单片机]
stm32串口DMA方式发送数据
DMA发送数据 启动DMA并发送完成后,产生DMA发送完成中断,在DMA中断服务函数中执行以下操作: 在数据发送缓冲区内放好要发送的数据(此数据缓冲区的首地址必须要在DMA初始化时写入到DMA配置中去) 将数据缓冲区内要发送的数据字节数传给DMA通道(串口发送和接收不是同一个通道) 开启DMA,一旦开启,则DMA开始发送数据, 等待数据发送完成标志! 判断数据发送完成: 清DMA发送完成标志 关闭串口发送DMA通道 给前台(应用)程序设置一个软件标志位,说明数据发送完成。 DMA接收数据 串口接收DMA在初始化时就处于开启状态,一直等待数据的到来,串口中断IDLE在串口一直没有数据时,是不会产生的,
[单片机]
STM32串口DMA方式接收数据
一直以来都为串口接收数据所困扰: 1:如果用接收中断的话,每接收1byte就得中断一次。这样太消耗CPU资源! 2:如果用DMA方式接收数据,那么如何确定接收数据的长度又不好确定了。(比如GPRS模块AT命令的接收!) 3:DMA方式接收+定时器的超时中断。这样处理也比较复杂,需要开定时器,关定时器。。。。个人不喜欢!(ATMEL的ARM系列的串口倒是有硬件超时中断可以直接使用。我现在用AT91SAM7系列处理GPRS的AT命令就采用这种方式,挺好用。但是STM32就没有了,需要自己加定时器,还要硬件处理:RXD连接定时器的一个触发引脚!)。 所以之前用STM32接收串口数据都是采用接收中断,然后写入一个FIFO队列。然后在主函数
[单片机]
STM32的串口采用DMA方式接收数据测试
环境: 主机:WINXP 开发环境:MDK4.23 MCU:STM32F103CBT6 说明: 串口可以配置成用DMA的方式接收数据,不过DMA需要定长才能产生接收中断,如何接收可变长度的数据呢? 方法有以下3种: 1.将RX脚与一路时钟外部引脚相连,当串口一帧发完,即可利用此定时器产生超时中断.这个实时性较高,可以做到1个字节实时监测. 2.不改变硬件,开启一个定时器监控DMA接收,如果超时则产生中断.这个实时性不高,因为超时时间必须要大于需要接收帧的时间,精度不好控制. 3.STM32单片机有的串口可以监测总线是否处于空闲,如果空闲则产生中断.可以用它来监测DMA接收是否完毕.这种方式实时性很高. 本文采用
[单片机]
<font color='red'>STM32</font>的串口采用<font color='red'>DMA</font><font color='red'>方式</font>接收数据测试
STM32CUBEMX(2)--USART通过DMA方式接收不定长数据
概述 直接存储器访问(DMA)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速传输传输。这样节省的CPU资源可供其它操作使用。 DMA允许在后台执行数据传输,无需Cortex-MO处理器干预。在此操作过程中,主处理器可以执行其它任务,仅当整个数据块需要处理时,才会中断主处理器。这样即使传输大量数据也不会对系统性能造成太大影响。 DMA主要用于为不同的外设模块实现集中数据缓冲存储(通常在系统SRAM中)。与分布式解决方案(其中每个外设都需要实现自己的本地数据存储)相比,DMA解决方案在硅片成本和功耗方面的成本较低。 根据使用的产品型号的不同,有一个或两个DM
[单片机]
STM32CUBEMX(6)--移植雅特力AT32F403AVGT7,双串口通过DMA方式接收不定长数据
概述 本篇文章主要介绍如何使用STM32CubeMX移植到雅特力AT32F403AVGT7,并通过双串口输出打印。 直接存储器访问(DMA)用于在外设与存储器之间以及存储器与存储器之间提供高速数据传输。可以在无需任何CPU操作的情况下通过DMA快速传输传输。这样节省的CPU资源可供其它操作使用。 DMA允许在后台执行数据传输,无需Cortex-M3处理器干预。在此操作过程中,主处理器可以执行其它任务,仅当整个数据块需要处理时,才会中断主处理器。这样即使传输大量数据也不会对系统性能造成太大影响。 DMA主要用于为不同的外设模块实现集中数据缓冲存储(通常在系统SRAM中)。与分布式解决方案(其中每个外设都需要实现自己的本地数据存储
[单片机]
STM32CUBEMX(6)--移植雅特力AT32F403AVGT7,双串口通过<font color='red'>DMA</font><font color='red'>方式</font>接收不定长数据
串口DMA方式发送&接收
串口DMA方式收发 笔者使用的是STM32F407VET6,共包含6路串口,页尾处程序已将全部串口的DMA收发配置完成,本文仅以串口1为例进行讲解。(查看代码可直接跳至第二节或页尾处下载) 1 STM32F4 DMA 简介 DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。 STM32F4 最多有 2 个 DMA 控制器(DMA1 和 DMA2),共 16 个数据流(每个控制器 8 个),每一个 DMA 控制器都用
[单片机]
串口<font color='red'>DMA</font><font color='red'>方式</font>发送&接收
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved