stm32.cube(九)——HAL.DMA

发布者:rho27最新更新时间:2018-05-01 来源: eefocus关键字:stm32  cube  HAL  DMA 手机看文章 扫描二维码
随时随地手机看文章

一、前言

DMA会在不同的寄存器/ram/存储设备之间建立通道,自动传输数据,以达到解放CPU的目的。

比如你想用DAC模块去输出一段特定的波形,就要让CPU将预设的数值不断写入DAC的寄存器。这时CPU被DAC任务长期占用,系统处理其他任务和响应其他事件的能力被大幅降低。

在实际应用里,经常有一些繁重的读写操作。这些操作不需要经过计算,却依然占用了大量的CPU资源,遇到这种情况就要考虑使用DMA了。

我开发板上的stm芯片上共有7个dma通道,它可以建立7个DMA连接。但是DMA控制器只有一个,所以同时只能有一个DMA连接被相应。

二、DMA的初始化

针对每一个DMA频道,都要初始化它的控制寄存器,来看一下DMA的init结构体的原型:

/** 

  * @brief  DMA Configuration Structure definition  

  */

typedef struct

{

  uint32_t Direction;                 /*!< Specifies if the data will be transferred from memory to peripheral, 

                                           from memory to memory or from peripheral to memory.

                                           This parameter can be a value of @ref DMA_Data_transfer_direction */


  uint32_t PeriphInc;                 /*!< Specifies whether the Peripheral address register should be incremented or not.

                                           This parameter can be a value of @ref DMA_Peripheral_incremented_mode */


  uint32_t MemInc;                    /*!< Specifies whether the memory address register should be incremented or not.

                                           This parameter can be a value of @ref DMA_Memory_incremented_mode */


  uint32_t PeriphDataAlignment;       /*!< Specifies the Peripheral data width.

                                           This parameter can be a value of @ref DMA_Peripheral_data_size */


  uint32_t MemDataAlignment;          /*!< Specifies the Memory data width.

                                           This parameter can be a value of @ref DMA_Memory_data_size */


  uint32_t Mode;                      /*!< Specifies the operation mode of the DMAy Channelx.

                                           This parameter can be a value of @ref DMA_mode

                                           @note The circular buffer mode cannot be used if the memory-to-memory

                                                 data transfer is configured on the selected Channel */ 


  uint32_t Priority;                   /*!< Specifies the software priority for the DMAy Channelx.

                                            This parameter can be a value of @ref DMA_Priority_level */


} DMA_InitTypeDef;

  • Direction的值表示通道类型,外设到ram、ram到外设、ram到ram。

  • PeriphInc和MemInc表示外设和ram地址要不要递增。像上述的DAC例子,ram的地址一定是递增的,而外设寄存器的地址则无需递增。

  • PeriphDataAlignment和MemDataAlignment表示外设和ram的字节宽度,有一个字节,半字和全字。这将决定上面的增量模式里,一次读取数据的大小。

  • Mode有两种,普通和循环。普通模式下一次DMA请求处理完成后就不再传输数据。

  • Priority是DMA频道的优先级,一共4个,如果优先级相同,频道号小的通道率先被响应。

这些属性被设置完毕后,在Init函数里会将它写入控制寄存器。


  /* Get the CR register value */

  tmp = hdma->Instance->CCR;


  /* Clear PL, MSIZE, PSIZE, MINC, PINC, CIRC, DIR bits */

  tmp &= ((uint32_t)~(DMA_CCR_PL    | DMA_CCR_MSIZE  | DMA_CCR_PSIZE  | \

                      DMA_CCR_MINC  | DMA_CCR_PINC   | DMA_CCR_CIRC   | \

                      DMA_CCR_DIR));


  /* Prepare the DMA Channel configuration */

  tmp |=  hdma->Init.Direction        |

          hdma->Init.PeriphInc           | hdma->Init.MemInc           |

          hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment |

          hdma->Init.Mode                | hdma->Init.Priority;


  /* Write to DMA Channel CR register */

  hdma->Instance->CCR = tmp;  


三、DMA通道的建立

在初始化完毕后,只需要将源地址、起始地址、传输总长写入寄存器,再使能该频道即可。

HAL_StatusTypeDef HAL_DMA_Start (DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);

HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength);

这个DMA_HandleTypeDef *hdma是用C实现面向对象设计的一个典型的例子。当我们创建一个DMA频道时,必须要先建立一个DMA_HandleTypeDef类型的结构体变量,这个行为实际上就是创建了一个DMA类的实例。

typedef struct __DMA_HandleTypeDef

{  

  DMA_Channel_TypeDef   *Instance;                                                    /*!< Register base address                  */


  DMA_InitTypeDef       Init;                                                         /*!< DMA communication parameters           */ 


  HAL_LockTypeDef       Lock;                                                         /*!< DMA locking object                     */  


  HAL_DMA_StateTypeDef  State;                                                        /*!< DMA transfer state                     */


  void                  *Parent;                                                      /*!< Parent object state                    */  


  void                  (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);     /*!< DMA transfer complete callback         */


  void                  (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*!< DMA Half transfer complete callback    */


  void                  (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);    /*!< DMA transfer error callback            */


  __IO uint32_t         ErrorCode;                                                    /*!< DMA Error code                         */


} DMA_HandleTypeDef;   

这个结构体除了包含有init结构体、锁、DMA寄存器指针、状态变量、错误变量之外,还包含了一些callback函数的指针。它甚至有一个父类指针,只要将该指针指向一些adc、uart等外设的handle类,就等于完成了继承。


除了有init和start函数外、HAL里还提供常规的DMA中断处理函数,等待DMA传输,获取DMA状态的一些函数,结构上与前面的adc、flash等类似,就不做叙述了。


四、例子


来看一个串口用dma收发的例子。 

首先是写收发两个频道的控制寄存器: 

  /*##-3- Configure the DMA ##################################################*/

  /* Configure the DMA handler for Transmission process */

  hdma_tx.Instance                 = USARTx_TX_DMA_CHANNEL;

  hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;

  hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;

  hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;

  hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

  hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;

  hdma_tx.Init.Mode                = DMA_NORMAL;

  hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;


  HAL_DMA_Init(&hdma_tx);


  /* Associate the initialized DMA handle to the UART handle */

  __HAL_LINKDMA(huart, hdmatx, hdma_tx);


  /* Configure the DMA handler for reception process */

  hdma_rx.Instance                 = USARTx_RX_DMA_CHANNEL;

  hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;

  hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;

  hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;

  hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

  hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;

  hdma_rx.Init.Mode                = DMA_NORMAL;

  hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;


  HAL_DMA_Init(&hdma_rx);


  /* Associate the initialized DMA handle to the the UART handle */

  __HAL_LINKDMA(huart, hdmarx, hdma_rx);

然后掉用串口的DMA函数这个函数内部会调用DMA_START_IT()函数来进行DMA请求。


  /*##-2- Start the transmission process #####################################*/

  /* User start transmission data through "TxBuffer" buffer */

  if (HAL_UART_Transmit_DMA(&UartHandle, (uint8_t *)aTxBuffer, TXBUFFERSIZE) != HAL_OK)

  {

    /* Transfer error in transmission process */

    Error_Handler();

  }


  /*##-3- Put UART peripheral in reception process ###########################*/

  /* Any data received will be stored in "RxBuffer" buffer : the number max of

     data received is 10 */

  if (HAL_UART_Receive_DMA(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)

  {

    /* Transfer error in reception process */

    Error_Handler();

  }


上面的HAL_LINKDMA宏是用来关联两个类的。

#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD_, __DMA_HANDLE_)           \

                        do{                                                  \

                              (__HANDLE__)->__PPP_DMA_FIELD_ = &(__DMA_HANDLE_); \

                              (__DMA_HANDLE_).Parent = (__HANDLE__);             \

                          } while(0)


关键字:stm32  cube  HAL  DMA 引用地址:stm32.cube(九)——HAL.DMA

上一篇:S29GL128P norflash 读写擦除问题
下一篇:stm32.cube(十)——单HAL模块的结构

推荐阅读最新更新时间:2024-03-16 16:01

STM32控制16路舵机控制板PCA9685
介绍 PCA9685 是最新的快速模式 Plus(Fm+)系列中的一员。 Fm+器件可以提供更高的频率 (高达 1MHz)和更频繁(densely populated) 的总线操作(高达 4000pF)。 OE引脚一定要至低使能,或者直接接地 网上Arduino的教程很多,商家给的也是Arduino的驱动文件,那怎么在STM32上用呢? STM32与驱动板的连接 驱动板 STM32 VCC 3.3V GND GND SCL I2C_SCL SDA I2C_SDA OE GND(低电平) V+ 不接 V+可以不接而采用电源接线柱使用外部供电,用5V的充电宝即可 由于PCA9685是使用IIC的,那么如何使用I
[单片机]
stm32 keil中出现use of undeclared identifier' '的原因
出现这种情况,一般有以下几种原因: 1.变量未定义 2.未包含头文件 3.编译路径未包含该文件地址
[单片机]
<font color='red'>stm32</font> keil中出现use of undeclared identifier' '的原因
STM32实例之LED灯闪烁控制以及相关注意事项
在本实例中,主要是为了实现LED灯的闪烁。首先分析LED的驱动方式,本实验中使用的是OpenM3V,内置8个LED均采用灌流方式驱动(低电平亮)。如果想要实现其闪烁,则需要给相应端口持续不断的高低交替电平。 在软件结构设计中,加入使用LED8,则需要在PD7口不断的输出高电平和低电平。首先需要初始化系统时钟,然后再开始配置PD7作为输出使用,打开外设时钟最后控制PD7输出持续的高低轮流。 开始 - 配置系统时钟 - 配置PD7作为输出在打开PD外设时钟 - 置位PD7,熄灭LED8 - 延时程序 - 清PD7,点亮LED8 - 延时 - 置位PD7,以此开始循环闪烁。 以下给出具体的代码(代码运行在KEIL5上)。 在软件代码编写
[单片机]
STM32上电复位不正常 手动复位正常的原因
描述: STM32f0按键模块电路经常遇到上电不复位的情况,芯片采用的是stm32f030,按键模块采用ZLG7290芯片、4X4按键LED矩阵。ZLG7290是周立功公司专门开发的按键芯片,其最大可支持64个按键和64个LED,可通过I2C协议对其读写,ZLG7290与stm32f030复位电路采用datasheet参考电路,如下: 正常情况下,电路上电后会有一个LED全部点亮的过程,上电稳定延时后,LED自动全部熄灭,正常运行时,按下相应按键其对应的LED灯应被点亮。 问题: STM32复位电路内部有自带的上拉电阻,同时PVD的电源检测阈值采用的是默认值2.2V,采用电池供电,电压表测量引脚电压在2.8V左右,但是电路上电
[单片机]
<font color='red'>STM32</font>上电复位不正常 手动复位正常的原因
STM32驱动74hc595
#include bsp_74HC595.h #define HC595_DS PCout(13)// #define HC595_OE PAout(0)// #define HC595_ST_CP        PBout(9)// #define HC595_SH_CP PBout(8)// static void HC595_Delay(u32 t) { u32 i; while(t--) for (i = 0; i 1; i++); } void HC595_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_
[单片机]
STM32时钟理解
一、硬件上的连接问题 如果使用内部RC振荡器而不使用外部晶振,请按照如下方法处理: 1)对于100脚或144脚的产品,OSC_IN应接地,OSC_OUT应悬空。 2)对于少于100脚的产品,有2种接法: i)OSC_IN和OSC_OUT分别通过10K电阻接地。此方法可提高EMC性能。 ii)分别重映射OSC_IN和OSC_OUT至PD0和PD1,再配置PD0和PD1为推挽输出并输出'0'。此方法可以减小功耗并(相对上面i)节省2个外部电阻。 STM32时钟系统结构图 时钟是STM32单片机的脉搏,是单片机的驱动源。使用任何一个外设都必须打开相应的时钟。这样的好处就是,如果不使用一个外设的时候,就把它
[单片机]
<font color='red'>STM32</font>时钟理解
STM32高级定时器之时钟源
1、定时器时钟源框图 从图片中可以看到定时器有4个时钟源, (1)内部时钟 ;(2) 外部时钟模式1,定时器的通道1、2 ; (3) 外部时钟模式模式2 ,ETR脚 ; (4)内部触发输入 下面分别介绍这几种时钟源的详细配置 2、内部时钟 寄存器SMCR的SMS选择000 3、外部时钟模式1 中文手册已经有以通道2为例子详细说明,这里我以通道1来说明,算是对它的温故和补充 3.1 滤波器设置,我的理解这里的滤波,比如我这里配置为0001,不是说通道来了N=2个脉冲才算这个输入有效,而是脉冲到来后,延时N/Fsampling的时间,还是高电平,就认为这个脉冲有效 类似按键检测的延时去抖动,如果理解
[单片机]
<font color='red'>STM32</font>高级定时器之时钟源
完成stm32上HID的应用
这个是公司的项目,以前基于usb的虚拟串口来做上下位机通信,由于usb平时也就刷参数和调试之用,也不很常用,外面反应usb有时会连不上stm32控制器,心里一直认为不是大问题,后来反应的多了,就重视这个事情了,虽然usb平时不用,但是用的时候只要出现异常掉线,就再也连不上去了,特别影响用户对我们产品的信心。 在网上找了很多资料,到现在也只能隐约确定是stm32官方的虚拟串口库有问题,我发现不同的电源板都会影响到usb虚拟串口的枚举,于是下定决心使用其他方式来做上下位机的连接,排除了usb虚拟串口,眼前的选择只有两个 1)usblib,这个是针对usb的上位机库,可以玩很多的花样,但是需要开发者稍稍熟悉usb协议,而且这个需要在
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

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

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

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved