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的智能应用

推荐阅读最新更新时间:2024-11-01 22:09

stm32printf函数调用
一、对工程属性进行配置,详细步骤如下 1、首先要在你的main 文件中 包含“stdio.h” (标准输入输出头文件)。 2、在main文件中重定义 fputc 函数 如下: // 发送数据 int fputc(int ch, FILE *f) { USART_SendData(USART1, (unsigned char) ch);// USART1 可以换成 USART2 等 while (!(USART1- SR & USART_FLAG_TXE)); return (ch); } 这样在使用printf时就会调用自定义的fputc函数,来发送字符。 3、在工程属性的 “Targe
[单片机]
stm32之USART串口配置
概念 在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。(好吧我也不是很懂,暂且贴上官方定义,各位看官自己悟吧) 配置步骤 打开时钟(RCC配置) 由于UART的TX和RX和AFIO都挂在APB2桥上,因此采用固件库函数RCC_APB2PeriphClockCmd()进行初始化。UARTx需要分情况讨论,如果是UART1,则挂在APB2桥上,因此采用RCC_APB2PeriphClockCmd()进行初始化,其余的UART2~5均挂在APB1上。 GPIO配置 GPIO的属性包含在结构体GPIO_InitTypeD
[单片机]
STM32——GPIO输入模式下上拉和下拉的设置
GPIO处于输入模式下,下拉输入和上拉输入的相关配置如下图所示。需要注意的是,下拉输入和上拉输入是通过端口输出寄存器GPIOx_ODR来区分的。因此,在进行上拉/下拉输入配置时候,虽然对GPIO进行的关于输入的操作,但是仍要对和输出相关的寄存器ODR进行配置。 上述注意事项在实际代码编写时表现为: (1)库函数 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 下拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); GP
[单片机]
<font color='red'>STM32</font>——GPIO输入模式下上拉和下拉的设置
STM32复习笔记(十三)ADC模/数转换
一、ADC简介: Analog-to-Digital Converter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。 二、STM32F10x ADC特点: 12位逐次逼近型的模拟数字转换器。 最多带3个ADC控制器 最多支持18个通道,可最多测量16个外部和2个内部信号源。 支持单次和连续转换模式 转换结束,注入转换结束,和发生模拟看门狗事件时产生中断。 通道0到通道n的自动扫描模式 自动校准 采样间隔可以按通道编程 规则通道和注入通道均有外部触发选项 转换结果支持左对齐或右
[单片机]
<font color='red'>STM32</font>复习笔记(十三)ADC模/数转换
STM32中EXTI(外部中断)和NVIC(嵌套向量中断)的关系
NVIC是Cortex-M3核心的一部分,关于它的资料不在《STM32的技术参考手册》中,应查阅ARM公司的《Cortex-M3技术参考手册》 Cortex-M3的向量中断统一由NVIC管理 EXTI是ST公司在其STM32产品上扩展的外中断控制。它负责管理映射到GPIO引脚上的外中断和片内几个集成外设的中断(PVD,RTC alarm,USB wakeup,ethernet wakeup),以及软件中断。其输出最终被映射到NVIC的相应通道。因此,配置EXTI中断的过程必然包含对NVIC的配置,例如下面配置EXTI0的过程,就要首先配置EXTI控制器(使能相应的中断线,选择中断/事件模式,触发边沿极性),然后再配置NVIC控制器(
[单片机]
STM32 TIMER2的使用
配置定时100us的配置如下: TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TIM_DeInit(TIM2); TIM_TimeBaseStructure.TIM_Period = 1;//59999;//1199;//9999; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DI
[单片机]
在学习STM32时为什么要学习汇编?
不同的平台的汇编代码是不一样的,最早的汇编在50年代就发明了,比很多人的父母的年龄都大,老掉牙,不用学习怎么写汇编。一个公司有一个人知道怎么写汇编就够了。但要学习读汇编,为什么学习汇编? 1、性能 直接翻译为机器语言,性能最高。优秀的C语言效率只能达到汇编的80%左右。其他高级语言跟汇编一比差得更远。语言越高级性能越差。很多bootloader和BIOS用汇编写,汇编操作的是电脑,手机刚刚上电时,硬件和初始化的那些命令,它们的性能的要求比较高,效率高开机速度更快。 分析问题 个人认为,编程人与机器对话,我们写C,写JAVA,但是电脑并不认识这些语言,电脑只认识0和1;所以需要一个人来翻译这些语言,这个翻译官就是编译器,但是编译器不
[单片机]
在学习<font color='red'>STM32</font>时为什么要学习汇编?
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

更多每日新闻

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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