前言
本文主要接上篇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加大,分为了两个部分,利用半传输完成中断和传输完成中断来确定的当前操作的是缓冲区的前半部分还是后半部分,当对前半部分传输的时候,对后半部分进行读取求平均,当然也可以根据自己的需要对后半部分进行别的操作,这样就完成了双缓冲传输。