DMA的巨详细配置步骤(还不懂回家种田去)

首先介绍我的DMA使用场景“电阻ADC转化”。

从外界读取ADC的值,然后让DMA进行搬运至自己定义的数组当中,之后读取这个数据就可以了。

先介绍一个我的ADC结构体,也就是句柄(到时候对ADC的所有控制,就是操作这个句柄)

/**
  * @brief  ADC handle Structure definition  
  */ 
typedef struct __ADC_HandleTypeDef
{
  ADC_TypeDef                   *Instance;              /*! < ADC实例                           > */

  ADC_InitTypeDef               Init;                   /*! < 配置ADC初始化参数                 > */

  DMA_HandleTypeDef             *DMA_Handle;            /*! < 直接内存访问(DMA)操作的句柄结构 > */

  HAL_LockTypeDef               Lock;                   /*! < 锁的状态                          > */
  
  __IO uint32_t                 State;                  /*! < 表示ADC状态                       > */

  __IO uint32_t                 ErrorCode;              /*! < 存储ADC错误代码的32位寄存器       > */

#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
  void (* ConvCpltCallback)(struct __ADC_HandleTypeDef *hadc);              /*!< ADC conversion complete callback */
  void (* ConvHalfCpltCallback)(struct __ADC_HandleTypeDef *hadc);          /*!< ADC conversion DMA half-transfer callback */
  void (* LevelOutOfWindowCallback)(struct __ADC_HandleTypeDef *hadc);      /*!< ADC analog watchdog 1 callback */
  void (* ErrorCallback)(struct __ADC_HandleTypeDef *hadc);                 /*!< ADC error callback */
  void (* InjectedConvCpltCallback)(struct __ADC_HandleTypeDef *hadc);      /*!< ADC group injected conversion complete callback */       /*!< ADC end of sampling callback */
  void (* MspInitCallback)(struct __ADC_HandleTypeDef *hadc);               /*!< ADC Msp Init callback */
  void (* MspDeInitCallback)(struct __ADC_HandleTypeDef *hadc);             /*!< ADC Msp DeInit callback */
#endif /* USE_HAL_ADC_REGISTER_CALLBACKS */
}ADC_HandleTypeDef;

上面的代码中是ADC的结构体句柄,我简单写了注释(为什么简单?因为你别管,咱们重点是DMA)。

除了下面的这个元素(这TM是重点,下面仔细介绍,叫我懂王就好

  DMA_HandleTypeDef   *DMA_Handle;  /*! < 直接内存访问(DMA)操作的句柄结构 > */

如果你要问我,下面的感觉像函数一样的东西为什么没有中文注释,那我可以回答你:

那玩意我也不懂,所以你也可以不用管,完全不影响咱们的DMA讲解!懂了吗!

懂了就好,不用谢,叫我 “装-B-王”;

---------------------------------------------------------我去你的分割线---------------------------------------------

现在我们来看我说的DMA的结构体

typedef struct __DMA_HandleTypeDef
{
  DMA_Channel_TypeDef   *Instance;    /*! < 指向 DMA 通道的寄存器基址 > */
  
  DMA_InitTypeDef       Init;        /*! < 初始化 DMA 通道的配置参数 > */ 
  
  HAL_LockTypeDef       Lock;         /*! < 锁定 DMA 通道             > */  
  
  HAL_DMA_StateTypeDef  State;         /*! < 当前 DMA 传输的状态       > */
  
  void                  *Parent;                                                      /*! < 指向父对象的状态                            > */  
  
  void                  (* XferCpltCallback)( struct __DMA_HandleTypeDef * hdma);     /*! < 回调函数指针,当 DMA 传输完成时会被调用     > */
  
  void                  (* XferHalfCpltCallback)( struct __DMA_HandleTypeDef * hdma); /*! < 回调函数指针,当 DMA 传输完成一半时会被调用 > */
  
  void                  (* XferErrorCallback)( struct __DMA_HandleTypeDef * hdma);    /*! < 回调函数指针,当 DMA 传输发生错误时会被调用 > */

  void                  (* XferAbortCallback)( struct __DMA_HandleTypeDef * hdma);    /*! < 回调函数指针,当 DMA 传输被中止时会被调用   > */  
  
  __IO uint32_t         ErrorCode;                                                    /*! < 存储 DMA 传输的错误代码                     > */

  DMA_TypeDef            *DmaBaseAddress;                                             /*! < 指向 DMA 控制器的基址                       > */
  
  uint32_t               ChannelIndex;                                                /*! < 表示 DMA 通道的索引                         > */  

} DMA_HandleTypeDef;   

这个结构体就是需要你配置的东西,就是DMA的句柄,到时候所有的操作就是要操作这个结构体的对象(不是男女对象,想什么呢,注意点),对象不用我教你怎么定义了吧(结构体名 xx),别定义什么稀奇古怪的名字,求你了

现在来详细介绍每一个元素:

我靠,怎么就2个元素,其他的被我吃了,其他的不用配,最后直接初始化,tm的最后报错我把电脑吃了

  DMA_Channel_TypeDef   *Instance;    这个元素就是(DMA1 还是 DMA2)
  
  DMA_InitTypeDef       Init;      (一个结构体),下面是他的细分(去他的细分,模糊求生模式
  

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;

你肯定想说:我靠,怎么那么多英文,老Z要看中文,不要急,下面我就给你讲中文,Hold住

__HAL_RCC_DMA1_CLK_ENABLE();                                   //开启DMA1的时钟
dma1_handle.Instance                 = DMA1_Channel1;           //使用的DMA通道1
dma1_handle.Init.Direction           = DMA_PERIPH_TO_MEMORY;    //外存到内设
dma1_handle.Init.PeriphInc           = DMA_PINC_DISABLE;        //外设地址寄存器不递增--固定
dma1_handle.Init.MemInc              = DMA_MINC_ENABLE;         //内存地址寄存器自动递增,不递增的话若有新数据来就会直接在这个基础上直接覆盖
dma1_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据宽度--半字dma1_handle.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD; //内存数据宽度--半字
dma1_handle.Init.Mode                = DMA_NORMAL ;             //正常模式
dma1_handle.Init.Priority            = DMA_PRIORITY_MEDIUM;     //优先级--中等	
	 
 HAL_DMA_Init(&dma1_handle);
	
__HAL_LINKDMA(&adc1_handle,DMA_Handle,dma1_handle);             //ADC-DMA关联函数(三个参数分别为:adc句柄,adc结构体内dma名称,dma句柄)

上面的代码就是配置的具体过程,你只看结构体里面就是一坨大便,咱们具体来讲。

1. 要先开启时钟(时钟是什么破玩意?),就是钱,没钱咋改装车,你说是吧。

2.instance,就是选择DMA的通道,那么通道可以随便选择吗?你选1,选2,选到天王老子都没关系,但别重复哈。

3.Direction的情况:外设到内存,内存到外设(你就看这两种情况就行了)

4.PeriphInc:就是外设的地址变不变呢?大哥,你变了你还怎么读这个地方的ADC值,隔山打牛读吗,上面的DISABLE是啥意思不用我说了

5.MemInc:是我内存的地址,也就是说我的数据来了放在哪里呢?如果你不ENABLE,好比你现在有100个水缸,一个水缸已经满了,你还要一直往里面注水(就是传数据),溢出来了大哥,你不自增我算你是条汉子

6.PeriphDataAlignment和MemDataAlignment,这两个就是数字大小,字节还是字,你自己选,但是要一致。

7.Mode:正常模式和循环模式,正常的情况DMA只搬一次,你打死他不会再搬,如果你的ADC一直在变,你想让他一直搬,那你就要用连续模式

8.Priority:这个随便你怎么弄,优先级而已

配置以后,     HAL_DMA_Init(&dma1_handle);

这个语句才是真的初始化成功了,你创的不算,你是老大还是编译器是老大。

然后:    __HAL_DMA_ENABLE(&dma1_handle);        //只是使能DMA(没有运输)

这句话的意思是,我的DMA电源开了,还没开始搬哈,一个语句你还想干两件事,想得美

  • 15
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值