stm32专题十三:DMA(一)结构框图

DMA(data memory access)直接存储器访问,和串口USART、GPIO一样,也是一个外设。

把数据从一个地方搬到另一个地方,而不占用CPU。像串口发送数据,数据是一个一个的发。CPU首先把数据从内存(数组)拿到CPU内部的寄存器(CPU内部有一组暂存数据寄存器R1、R2、R3等),然后再发到串口的数据寄存器USART_DR,这些过程一直需要占用CPU。而当我们使用DMA时,如果CPU给DMA一个命令(把数据从内存发到串口),这时DMA就会实现这个功能,整个过程CPU是空闲的,可以去做其他事。比如,点亮一个LED,显示液晶屏LCD等

DMA支持3种模式:

  1. Memory to Memory (这里的Memory可以是Flash或内部SRAM);
  2. Memory to Peripheral,这个最典型的应用是串口发送;
  3. Peripheral to Memory,这个最典型的应用就是ADC。ADC采集的数据是保存在数据寄存器DR,然后要读到内存;

DMA1有7个通道,DMA2有5个通道。

开始DMA时,以内存—串口为例。首先串口向CPU发送请求,告诉DMA,把数据从内存搬到发送数据寄存器中。这个请求由其中的一方来产生(发送方),通道为传输数据的管道,

有一个比较有意思的问题,就是数据对齐。我们通常要求发送方和接收方的数据长度要一致(数据对齐),但有些时候我们又会使用到不一致的情况(比如8位数据发送到定时器16位CCR寄存器),这时候该怎么办?我看到基本上很少有这种问题的详细解释与实验验证,但其实在中文参考手册中是有相关描述的。可以看到,对于DMA对齐的数据,直接操作没有问题。而当DMA数据不对其,比如8位传到16位时,如 DMA读到的8位数据为B0,实际写入的会是00B0,高8位用0补齐。当16位转8位数据时,如DMA读到B1B0,在写入时会把高8位丢弃,只写入B0位。这跟C语言的强制类型转换一致

为了验证这个DMA数据不对齐配置是否正确,使用stm32CubeMX进行了一次测试(8位数据从内存传到16位CCR寄存器),配置如下,实验结果正确。

DMA每个通道具体对应的外设,可以看到,连接在同一通道上的外设,DMA请求同时只能有有一个生效。同理,DMA发送数据时,每个通道只能用作其中的一种。例如:DMA1通道1,任何时刻,只能作为ADC1 TIM2_CH3 TIM4_CH1中的一个数据管道。这里说的只是M-P和P-M,当使用M-M时,所有的通道都可以随意使用

当有多个DMA请求一起来,就需要仲裁器来仲裁,到底哪个DMA请求先执行,这个在中文参考手册中有很详细的说明,具体如下:

那么我们就知道了,当多个DMA请求一起来时,先比较软件阶段,这里由DMA_CCRx寄存器的PL位分成4个等级,根据软件等级可以确定发送顺序。如果情况更加复杂,如有7路DMA请求,此时4个等级明显不够用,一定会出现等级相同的情况。此时,则比较硬件通道编号,编号越小等级越高。

标准固件库中的dma初始化结构体:

/** 
  * @brief  DMA Init structure definition
  */

typedef struct
{
  // 外设基地址
  uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Channelx. */

 // 存储器基地址  
uint32_t DMA_MemoryBaseAddr;     /*!< Specifies the memory base address for DMAy Channelx. */

// DMA传输方向(M-P  P-M)  
uint32_t DMA_DIR;                /*!< Specifies if the peripheral is the source or destination.
                                        This parameter can be a value of @ref DMA_data_transfer_direction */

// DMA传输的数据量(注意,不是字节数,如4个uint16_t,就是4,不是8)
uint32_t DMA_BufferSize;         /*!< Specifies the buffer size, in data unit, of the specified Channel. 
                                        The data unit is equal to the configuration set in DMA_PeripheralDataSize
                                        or DMA_MemoryDataSize members depending in the transfer direction. */

// 外设地址递增  
uint32_t DMA_PeripheralInc;      /*!< Specifies whether the Peripheral address register is incremented or not.
                                        This parameter can be a value of @ref DMA_peripheral_incremented_mode */

// 存储器地址递增  
uint32_t DMA_MemoryInc;          /*!< Specifies whether the memory address register is incremented or not.
                                        This parameter can be a value of @ref DMA_memory_incremented_mode */

  // DMA外设数据宽度(字节,半字,字),通常要与存储器数据宽度对齐(也可以不对齐)
  // 如果数据宽度不对齐,按前面说的C语言规则处理
  uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width.
                                        This parameter can be a value of @ref DMA_peripheral_data_size */

  // 存储器数据宽度
  uint32_t DMA_MemoryDataSize;     /*!< Specifies the Memory data width.
                                        This parameter can be a value of @ref DMA_memory_data_size */

  // DMA模式(普通和循环模式)
  uint32_t DMA_Mode;               /*!< Specifies the operation mode of the DMAy Channelx.
                                        This parameter can be a value of @ref DMA_circular_normal_mode.
                                        @note: The circular buffer mode cannot be used if the memory-to-memory
                                              data transfer is configured on the selected Channel */

  // DMA优先级(最高/高/中/低)
  uint32_t DMA_Priority;           /*!< Specifies the software priority for the DMAy Channelx.
                                        This parameter can be a value of @ref DMA_priority_level */

  // 是否使能存储器到存储器模式
  uint32_t DMA_M2M;                /*!< Specifies if the DMAy Channelx will be used in memory-to-memory transfer.
                                        This parameter can be a value of @ref DMA_memory_to_memory */
}DMA_InitTypeDef;

DMA可以设置三种中断,传输完成中断、传输过半中断、传输错误中断。

/**
  * @brief  Enables or disables the specified DMAy Channelx interrupts.
  * @param  DMAy_Channelx: where y can be 1 or 2 to select the DMA and 
  *   x can be 1 to 7 for DMA1 and 1 to 5 for DMA2 to select the DMA Channel.
  * @param  DMA_IT: specifies the DMA interrupts sources to be enabled
  *   or disabled. 
  *   This parameter can be any combination of the following values:
  *     @arg DMA_IT_TC:  Transfer complete interrupt mask
  *     @arg DMA_IT_HT:  Half transfer interrupt mask
  *     @arg DMA_IT_TE:  Transfer error interrupt mask
  * @param  NewState: new state of the specified DMA interrupts.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState)
{
  /* Check the parameters */
  assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));
  assert_param(IS_DMA_CONFIG_IT(DMA_IT));
  assert_param(IS_FUNCTIONAL_STATE(NewState));
  if (NewState != DISABLE)
  {
    /* Enable the selected DMA interrupts */
    DMAy_Channelx->CCR |= DMA_IT;
  }
  else
  {
    /* Disable the selected DMA interrupts */
    DMAy_Channelx->CCR &= ~DMA_IT;
  }
}

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值