定时器下ADC采集向内存双缓冲DMA传输

定时器下ADC采集向内存双缓冲DMA传输


前言

本文主要接上篇ADC多通道采集DAM传输配置,配置大致一样,这篇主要讲的是单通道采集双缓冲传输。


先说一下为什么使用双缓冲,一般来说我们不会同时对一个内存区域同时进行两种或者多种操作,但是很多场景需要我们进行同时操作的,我们不可能时候都等对一块内存区域等它写满之后再往Flash上搬,这时候我们可与i利用双缓冲模式,即当缓冲buff1满了先往buff2上写,然后操作buff1,这样就解决了上面的问题。
DMA自带的有双缓冲模式,但是我在正点原子的F429上使用 HAL_DMAEx_MultiBufferStart_IT(&DAM_Handal,(uint32_t )&ADC1_Handler.Instance->DR,(uint32_t )ADCxValues1,(uint32_t )ADCxValues2,4);配置使用未能成功,学艺不精,我就软件实现了一个ADC采集双缓冲模式,仅供参考。

配置

配置与ADC多通道采集DAM传输配置差别不大,这里把代码复制过来:

#include "adc.h"
#include "delay.h"
#include "time.h"


DMA_HandleTypeDef          DAM_Handal;
ADC_HandleTypeDef           ADC1_Handler;//ADC句柄


 uint8_t   ADCxValues[4] = {0};





void MY_ADC_Init(void)
{ 
    ADC1_Handler.Instance                             =ADC1;
    ADC1_Handler.Init.ClockPrescaler                =ADC_CLOCK_SYNC_PCLK_DIV4;   //4分频,ADCCLK=PCLK2/4=90/4=22.5MHZ
    ADC1_Handler.Init.Resolution                    =ADC_RESOLUTION_8B;             //8位模式
    ADC1_Handler.Init.DataAlign                     =ADC_DATAALIGN_RIGHT;           //右对齐
    ADC1_Handler.Init.ScanConvMode                 =DISABLE;                                    //非扫描模式
    ADC1_Handler.Init.EOCSelection                 =DISABLE;                                   //关闭EOC中断
    ADC1_Handler.Init.ContinuousConvMode           =DISABLE;                                   //关闭连续转换
    ADC1_Handler.Init.NbrOfConversion              =1;                                             //1个转换在规则序列中 也就是只转换规则序列1 
    ADC1_Handler.Init.DiscontinuousConvMode        =DISABLE;                                //禁止不连续采样模式
    ADC1_Handler.Init.NbrOfDiscConversion          =0;                                            //不连续采样通道数为0
    ADC1_Handler.Init.ExternalTrigConv            =ADC_EXTERNALTRIGCONV_T3_CC1;       //TIM3_Chanal1触发
    ADC1_Handler.Init.ExternalTrigConvEdge        =ADC_EXTERNALTRIGCONVEDGE_RISING;//上升沿触发
    ADC1_Handler.Init.DMAContinuousRequests       =ENABLE;                               //开启DMA请求
    HAL_ADC_Init(&ADC1_Handler);                                                                 //初始化 

   Get_Adc() ;
}

//ADC底层驱动,引脚配置,时钟使能
//此函数会被HAL_ADC_Init()调用
//hadc:ADC句柄
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_ADC1_CLK_ENABLE();            //使能ADC1时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();			     //开启GPIOA时钟
	
    GPIO_Initure.Pin              =GPIO_PIN_7|GPIO_PIN_4;       //PA5
    GPIO_Initure.Mode             =GPIO_MODE_ANALOG;     //模拟
    GPIO_Initure.Pull             =GPIO_NOPULL;          //不带上下拉
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}





void myDMA_init(DMA_Stream_TypeDef *DMAa_Streamb, u32 chx )
{
		if((u32)DMAa_Streamb>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
	{
        __HAL_RCC_DMA2_CLK_ENABLE();//DMA2时钟使能	
	}else 
	{
        __HAL_RCC_DMA1_CLK_ENABLE();//DMA1时钟使能 
	}
   
	
	DAM_Handal.Instance                              = DMA2_Stream4;                            //DMA数据流
	DAM_Handal.Init.Channel                          = chx;                                               //DMA数据流通道
  DAM_Handal.Init.Direction                          = DMA_PERIPH_TO_MEMORY; 	         //传输方向
  ADC1_Handler.Init.ClockPrescaler                   =ADC_CLOCK_SYNC_PCLK_DIV4;        //时钟分频
	DAM_Handal.Init.PeriphInc                        = DMA_PINC_DISABLE;
	DAM_Handal.Init.MemInc                           = DMA_MINC_ENABLE;
	DAM_Handal.Init.PeriphDataAlignment              = DMA_MDATAALIGN_BYTE;
  DAM_Handal.Init.MemDataAlignment                   =DMA_MDATAALIGN_BYTE; 
  DAM_Handal.Init.Mode                               =DMA_CIRCULAR;                            //外设循环模式
	DAM_Handal.Init.Priority                         =DMA_PRIORITY_MEDIUM;               //中等优先级
	DAM_Handal.Init.FIFOMode                         =DMA_FIFOMODE_DISABLE;              
	DAM_Handal.Init.FIFOThreshold                    =DMA_FIFO_THRESHOLD_FULL;      
	DAM_Handal.Init.MemBurst                         =DMA_MBURST_SINGLE;                 //存储器突发单次传输
	DAM_Handal.Init.PeriphBurst                      =DMA_PBURST_SINGLE;              //外设突发单次传输


	/* 开启 DMA1 Stream1 的中断 */
//HAL_DMA_IRQHandler (&DAM_Handal);

__HAL_LINKDMA(&ADC1_Handler,DMA_Handle,DAM_Handal);    //将DMA与ADC1联系起来(发送DMA)
	
	

    HAL_DMA_DeInit(&DAM_Handal);   
    HAL_DMA_Init(&DAM_Handal);

}



//获得ADC值
//ch: 通道值 0~16,取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
//返回值:转换结果
void  Get_Adc() 
{
    ADC_ChannelConfTypeDef ADC1_ChanConf;
    
    ADC1_ChanConf.Channel           =7;                                   //通道
    ADC1_ChanConf.Rank              =1;                                   //第1个序列,序列1
    ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_480CYCLES;  //采样时间
    ADC1_ChanConf.Offset             =0;                 
    HAL_ADC_ConfigChannel(&ADC1_Handler,&ADC1_ChanConf);        //通道配置
	
    HAL_ADC_Start(&ADC1_Handler);                               //开启ADC
	
   myDMA_init(DMA2_Stream4,DMA_CHANNEL_0);

   HAL_ADC_Start_DMA(&ADC1_Handler, (uint32_t *)ADCxValues, 4);
    
//   HAL_DMAEx_MultiBufferStart_IT(&DAM_Handal,(uint32_t )&ADC1_Handler.Instance->DR,(uint32_t )ADCxValues1,(uint32_t )ADCxValues2,4);
}


void Get_Adc_Average(uint8_t *buf,uint8_t flag)
{
		if(flag == 1)
					buf[0] =( ADCxValues[0]+ADCxValues[1])/2;
		if(flag == 0)
				 buf[1] =( ADCxValues[2]+ADCxValues[3])/2;
} 


不同的是把传输缓存区ADCxValues加大,分为了两个部分,利用半传输完成中断和传输完成中断来确定的当前操作的是缓冲区的前半部分还是后半部分,当对前半部分传输的时候,对后半部分进行读取求平均,当然也可以根据自己的需要对后半部分进行别的操作,这样就完成了双缓冲传输。

STM32F4定时器触发ADC DMA双缓冲的方法如下: 首先,配置定时器,设置计数器的自动重装载值和预分频器,以确定定时器的计数周期和触发频率。 然后,配置ADC,选择需要转换的通道和采样速率,使其准备好接收转换请求。 接下来,配置DMA,设置双缓冲模式,分配两个数据缓冲区,一个用于DMA传输期间,一个用于处理数据。设置DMA传输长度和目的地址,以便将ADC数据直接传输到缓冲区。 在启动定时器之前,启动DMA传输,并将DMA请求与定时器的触发事件相关联。这样,当定时器满足触发条件时,ADC将自动进行转换,并且转换完成的数据将通过DMA传输到缓冲区。 在主循环中,检测DMA传输完成事件,并根据需要处理接收到的数据。此时,可以开始对缓冲区中的数据进行处理,例如计算平均值、滤波或其他处理方式。 同时,在DMA传输完成后,需要交换两个缓冲区的角色,使之成为当前处理数据的缓冲区。这样,当下一次DMA传输完成时,可以将数据传输到另一个缓冲区,以保证数据的连续传输。 最后,根据需要,可以选择暂停或停止定时器DMA传输,以便在不需要时节省功耗或进行其他操作。 总结:通过配置STM32F4的定时器ADCDMA,可以实现定时触发ADC转换并通过DMA进行双缓冲传输的功能。这种方式可以提高数据处理的效率和精度,并且减少CPU的负载。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值