STM32F4的ADC采样——多通道、DMA、定时器触发

项目中需要对三个通道的电压进行一定频率的AD采样,由于采样过程贯穿整个任务,为了使采样过程尽可能不占用CPU资源,采用定时器触发的多通道ADC扫描采样,且采样数据由DMA传到RAM中的缓存。
这样做有以下几个好处:1、由定时器触发ADC采样,这样采样的频率可控,且定时器触发不会占用任何CPU资源;2、DMA进一步降低了任务对CPU的占有率。

一、硬件原理简介

1.1 ADC

ADC的规则通道扫描采样不再赘述,配置好规则通道后,可以采用软件触发的方式开启AD转换,也可通过外部触发,如下图所示。可以通过定时器以及外部中断方式触发。在这里插入图片描述
使用定时器触发时,最好实现的为TIM3_TRGO事件,这个事件将在下面介绍。也就是说当ADC转换配置为不连续模式时,每发生一次TIM3_TRGO事件,就会触发ADC进行一次规则通道的转换。

1.2 定时器

前面提到TIM3_TRGO事件,那什么是TIM3_TRGO呢。看下图,可以把它理解为一个定时器内部输出的信号,当满足一定条件时他就输出一个信号到其他外设,从而触发其他外设的某些操作。运用在ADC中即是触发ADC的一次规则通道转换。在这里插入图片描述

1.3 DMA

其实这个没什么好说的,就是配置好,根据你所用的ADC来选择DMA设备,且配置相应的数据流和通道就好了。具体如何选择数据流和通道,看下图。
在这里插入图片描述
在这里插入图片描述

二、软件配置

提到外设的配置,怎么能少的了STM32Cube这个神器呢,了解了以上硬件原理后,我们可以使用STM32Cube轻松配置需要使用的外设,无非就三个外设——ADC、DMA、TIM。

2.1 ADC配置

这里我选用了ADC1的0、1、2三个通道作为采样通道在这里插入图片描述
到外设配置里,如下图配置,打箭头的位置需要注意:1、由于实用的是定时器触发的AD转换,故 连续模式要disable,这样才能定时器触发一次就转换一次选中的3个规则通道;2、由于是多通道,所以要开启扫描模式;3、使用了DMA;4、外部触发方式选择TIM2的Trigger Out event,就是一直在说的TRGO。其他的诸如分频、左右对齐、AD转换位数、转换周期等都不是重点。
在这里插入图片描述
同时,由于使用了DMA,故在上图的DMA Setting选项卡中做如下设置:1、根据DMA硬件原理中的DMA映射选择DMA2的数据流0,通道在这里没有体现,应该是通道0,当STM32Cube生成代码时可以看到已经配置好了;2、开启循环模式,否则一次DMA转换完成后就停止了;3、由于有三个通道,一轮ADC转换完成后会有三个采样值,这三个采样值将依次触发DMA请求,所以需要设置DMA内存地址递增,否则1号通道的值就会覆盖0号的值,2号的值又会覆盖1号的值;4、由于STM32的ADC最大就是12位,所以配置为半字(16位)足够。在这里插入图片描述

2.2 TIM配置

这里使用了TIM2,配置如下。实验中经过时钟树分频后,到TIM2的时钟为60MHz,此时配置TIM2预分频为35、重载值为375的向上计数模式,计数器每溢出一次就会产生一个更新事件。而按图中配置,更新事件将会触发TRGO信号。那么此时的采样频率为60MHz/35/375 = 3200Hz,也就是1秒钟触发3200次3通道的转换。在这里插入图片描述

2.3 DMA配置

DMA没啥配置。。。因为在ADC配置中已经都配置好了,需要注意三点:1、就是要记得开启DMA中断,并在中断服务函数中及时的对AD采样值处理;2、开始DMA的ADC转换:HAL_ADC_Start_DMA(&hadc1,buffer,3),buffer即为DMA接收缓存,3表示DMA传输的数据大小,即传输3个半字后就产生传输完成中断;3、不用DMA的传输完成一半中断的话记得关掉,以免DMA中断服务被没必要的调用。

最后.上代码

当然,这些代码都是STM32Cube生成的,你需要做的仅仅就是写一个好用的DMA中断服务函数,具体流程就是判断来了传输完成中断->请标志位->将DMA缓存中的数据拷贝到数据处理的缓存,然后做什么就有你而定了。

void MX_GPIO_Init(void)
{

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();

}

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

/* ADC1 init function */
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig;

/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
*/

hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 3;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
*/

sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
*/

sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

/**Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
*/

sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

}

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */

/* USER CODE END ADC1_MspInit 0 /
/
ADC1 clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();

/**ADC1 GPIO Configuration    
PA0-WKUP     ------> ADC1_IN0
PA1     ------> ADC1_IN1
PA2     ------> ADC1_IN2 
*/
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA2_Stream0;
hdma_adc1.Init.Channel = DMA_CHANNEL_0;
hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc1.Init.Mode = DMA_CIRCULAR;
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
  _Error_Handler(__FILE__, __LINE__);
}

__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);

/* USER CODE BEGIN ADC1_MspInit 1 */

/* USER CODE END ADC1_MspInit 1 */
}
}

void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{

if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */

/* USER CODE END ADC1_MspDeInit 0 /
/
Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();

/**ADC1 GPIO Configuration    
PA0-WKUP     ------> ADC1_IN0
PA1     ------> ADC1_IN1
PA2     ------> ADC1_IN2 
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2);

/* ADC1 DMA DeInit */
HAL_DMA_DeInit(adcHandle->DMA_Handle);

/* USER CODE BEGIN ADC1_MspDeInit 1 */

/* USER CODE END ADC1_MspDeInit 1 /
}
}
void MX_DMA_Init(void)
{
/
DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();

}
TIM_HandleTypeDef htim2;

/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;

htim2.Instance = TIM2;
htim2.Init.Prescaler = 35 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 375 - 1 ;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
_Error_Handler(FILE, LINE);
}

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */

/* USER CODE END TIM2_MspInit 0 /
/
TIM2 clock enable /
__HAL_RCC_TIM2_CLK_ENABLE();
/
USER CODE BEGIN TIM2_MspInit 1 */

/* USER CODE END TIM2_MspInit 1 */
}
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspDeInit 0 */

/* USER CODE END TIM2_MspDeInit 0 /
/
Peripheral clock disable /
__HAL_RCC_TIM2_CLK_DISABLE();
/
USER CODE BEGIN TIM2_MspDeInit 1 */

/* USER CODE END TIM2_MspDeInit 1 */
}
}

  • 11
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: STM32F4ADC采样使用单通道、DMA定时器触发FFT是一种常见的应用场景。 首先,单通道表示只使用一个ADC通道进行采样STM32F4系列微控制器通常具有多个ADC通道,可以选择适合的通道进行采样。通过配置ADC的控制寄存器和通道选择寄存器,可以设置ADC的工作模式和采样通道。 接下来,DMA(Direct Memory Access)是一种数据传输方式,可以在不经过CPU的情况下将数据从ADC缓冲区传输到存储器中。使用DMA可以提高系统性能,减轻CPU的负担。在配置DMA时,需要设置DMA的起始地址和目标地址,使得ADC采样数据可以直接传输到存储器中。 然后,定时器触发是指使用定时器的计时功能来触发ADC采样。通过配置定时器的计数器、预分频器和计时器模式,可以设置ADC采样频率和采样间隔。 最后,FFT(快速傅里叶变换)是一种信号处理算法,可以将时域信号转换为频域信号。在采样数据传输到存储器后,可以使用FFT算法对采样数据进行处理,提取出频域信息。 综上所述,STM32F4ADC采样单通道、DMA定时器触发FFT的应用流程如下:首先,选择合适的ADC通道并配置ADC的控制寄存器和通道选择寄存器。接着,配置DMA的起始地址和目标地址,使得ADC采样数据可以直接传输到存储器。然后,配置定时器的计数器、预分频器和计时器模式,设置ADC采样频率和采样间隔。最后,将采样数据传输到存储器后,使用FFT算法对采样数据进行处理,提取出频域信息。这种应用场景可以实现对信号的快速采样和频谱分析,广泛应用于音频信号处理、通信系统等领域。 ### 回答2: stm32f4ADC采样是指通过ADC模块对外部模拟信号进行转换,并将转换结果存储在内部寄存器中。以下是以单通道、DMA定时器触发FFT为例的ADC采样过程的详细描述: 首先,需要配置ADC模块的参数。可以选择单通道采样,即只使用一个模拟信号通道进行采样。可以选择采样率和采样精度,并设置对应的转换模式。 然后,需要配置DMA通道,以实现ADC数据的直接存储。DMA通道负责从ADC的数据寄存器中读取转换结果,并将其存储到指定的存储器区域中。通过使用DMA,可以在ADC转换过程中同时进行其他任务,提高采样效率。 接下来,需要配置一个定时器触发ADC的转换。定时器可以生成一个周期性的触发信号,用于精确控制采样的时间间隔。通过将定时器ADC触发源相连接,可以在每个定时器触发事件上开始一次ADC转换。 最后,可以将采样到的数据应用于FFT算法。FFT即快速傅里叶变换,可以将时域信号转换为频域信号。通过对ADC采样得到的数据进行FFT分析,可以获取信号的频谱信息,用于进一步的数据处理和分析。 综上所述,通过配置ADC模块的参数、设置DMA传输和定时器触发,可以实现stm32f4ADC单通道采样DMA传输和定时器触发FFT分析。通过这种方式,可以有效地进行模拟信号的采样和频域分析,用于各种应用场景中。 ### 回答3: STM32F4系列的ADC采样单通道、DMA定时器触发FFT是一种常见的硬件实现方法,适用于实时信号分析和处理的应用场景。 首先,STM32F4系列的微控制器内部集成了一种精确的ADC模块,可实现模拟信号的数字化转换。采样单通道即表示一次只对一个模拟输入通道进行采样,这有利于简化系统设计和提高采样精度。 其次,通过使用DMA(直接内存访问)控制器,可以实现高效的数据传输。DMA可以在ADC转换完成后,自动将采样数据从ADC模块中读出,并传输到指定的存储区域(如数组或缓冲区)。这样可以减少CPU的负担,提高系统的实时性和效率。 同时,使用定时器触发ADC转换,可以实现定时采样。通过配置定时器的参数,如采样频率、采样时间等,可以实现对模拟信号的周期性采样。这对于实时信号分析和处理非常重要,可以确保采样数据的一致性和准确性。 最后,采样后的数据可以通过FFT(快速傅里叶变换)算法进行频谱分析。FFT算法可以将时域上的采样数据转换为频域上的频谱信息,从而可以分析信号的频率成分和谱线。这对于信号处理和频谱显示非常有用,可以帮助工程师更好地了解信号的特性和用途。 总之,STM32F4ADC采样单通道、DMA定时器触发FFT是一种有效的硬件实现方法,适用于实时信号分析和处理的应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值